HDU 5441 Travel(求各个集合里的秩能组合多少对,只能在一个集合里组合)

题目地址:点击打开链接

题意:每2个城市间有一个权值,还有每次询问会给一个权值,城市间的权值小于询问的权值即可以走,问某个人能走多少对城市,注意(a,b),(b,a)算一对不同的城市

思路:刚开始用数组存储点和点的距离,然后每次搜强连通分量,搜出来强连通分量的个数不就是每个集合里面的个数么,假如说一个集合里面有n个值,那么总共有n(n-1)对,好理解吧,每个城市和其余城市连一次,不用除以2,因为(a,b),(b,a)算一对不同的城市,(可怜的我刚开始脑残,还还搞个函数求2C(n,2)的值,其实就是A(n,2),我擦),没想到直接MLE了,数组太大了,只能开数组保存每条边的信息,最后搞得太复杂,测试数据没过

下来看别人用并查集顿时傻逼了,我搜索也不就是为了求集合个数么,直接并查集不就搞定了,高兴的太早了,又T了好几次,对时间卡的特别严,在代码里细细分析

错误代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <cstring>
#include <climits>
#include <cmath>

using namespace std;

int map1[20010][20010];
int visit[20010];
int n,maxq;

int dfs(int x)
{
    visit[x] = 1;
    int i,sum = 0;
    for(i=1; i<=n; i++)
    {
        if(map1[x][i] < maxq && !visit[i])
        {
            sum++;
            sum += dfs(i);
        }
    }
    return sum;
}

int main()
{
    int t,m,q;
    int a,b,c,l;
    int i,j,max1;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d",&n,&m,&q);
        for(i=1; i<=n; i++)
        {
            for(j=1; j<=n; j++)
            {
                map1[i][j] = INT_MAX;

            }
        }
        for(i=0; i<m; i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            map1[a][b] = c;
            map1[b][a] = c;
        }
        for(i=0; i<q; i++)
        {
            max1 = 0;
            memset(visit,0,sizeof(visit));
            scanf("%d",&maxq);
            for(j=1; j<=n; j++)
            {
                if(!visit[j])
                {
                    l = dfs(j);
                    if(l > max1)
                        max1 = l;
                }
            }
            n = (long long)n;
            max1 = (long long)max1;
            printf("%I64d\n",2*myc(max1+1,2));
        }
    }
    return 0;
}

方法是错的,思路也是错的

超时代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <cstring>
#include <climits>
#include <cmath>

using namespace std;

struct node
{
    int value;
    int left;
    int right;
    bool operator < (const node &a) const
    {
        return value < a.value;
    }
}a[100010];

int pre[20010],num[20010];

int find(int x)
{
	int r = x,i = x,j;
	while(pre[r] != r)
	{
		r = pre[r];
	}
	while(pre[i] != r)//路径压缩
	{
		j = pre[i];
		pre[i] = r;
		i = j;
	}
	return r;
}

void join(int x,int y)
{
    int p = find(x);
	int q = find(y);
	if(p != q)
    {
        pre[p] = q;
        num[q] += num[p];
    }
}

int main()
{
    int t,i,j;
    int n,m,q;
    int x,y;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d",&n,&m,&q);
        for(i=0; i<m; i++)
        {
            scanf("%d%d%d",&a[i].left,&a[i].right,&a[i].value);
        }
        sort(a,a+m);
        for(i=0; i<q; i++)
        {
            int maxq;
            scanf("%d",&maxq);
            for(j=1; j<=n; j++)
            {
                pre[j] = j;
                num[j] = 1;
            }
            for(j=0; j<m; j++)
            {
                if(a[j].value < maxq)
                {
                    x = a[j].left;
                    y = a[j].right;
                    join(x,y);
                }
                if(a[j].value >= maxq)
                {
                    break;
                }
            }
            int max1 = 0;
            for(j=1; j<=n; j++)
            {
                if(pre[j] == j)
                    max1 += num[j] * (num[j] - 1);
            }
            printf("%d\n",max1);
        }
    }
    return 0;
}

这还是优化过的代码第一次都没对每条边的值进行排序

超时代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <cstring>
#include <climits>
#include <cmath>

using namespace std;

struct node
{
    int value;
    int left;
    int right;
    bool operator < (const node &a) const
    {
        return value < a.value;
    }
} a[100010];

struct ask
{
    int id;
    int value;
    bool operator < (const ask &a) const
    {
        return value < a.value;
    }
} b[5010];

int pre[20010],num[20010];

int find(int x)
{
    int r = x,i = x,j;
    while(pre[r] != r)
    {
        r = pre[r];
    }
    while(pre[i] != r)//路径压缩
    {
        j = pre[i];
        pre[i] = r;
        i = j;
    }
    return r;
}

void join(int x,int y)
{
    int p = find(x);
    int q = find(y);
    if(p != q)
    {
        pre[p] = q;
        num[q] += num[p];
    }
}

int main()
{
    int t,i,j;
    int n,m,q;
    int x,y;
    int ans[5010];
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d",&n,&m,&q);
        for(i=0; i<m; i++)
        {
            scanf("%d%d%d",&a[i].left,&a[i].right,&a[i].value);
        }
        sort(a,a+m);
        for(j=1; j<=n; j++)
        {
            pre[j] = j;
            num[j] = 1;
        }
        for(i=0; i<q; i++)
        {
            scanf("%d",&b[i].value);
            b[i].id = i;
        }
        sort(b,b+q);
        j = 0;
        for(i=0; i<q; i++)
        {
            for(; j<m; j++)
            {
                if(a[j].value < b[i].value)
                {
                    x = a[j].left;
                    y = a[j].right;
                    join(x,y);
                }
                if(a[j].value >= b[i].value)
                {
                    break;
                }
            }
            int max1 = 0;
            for(j=1; j<=n; j++)
            {
                if(pre[j] == j)
                    max1 += num[j] * (num[j] - 1);
            }

            ans[b[i].id] = max1;
        }

        for(j = 0; j < q; j++)
            printf("%d\n",ans[j]);
    }
    return 0;
}

这是第二次优化过的,注意看j=0已经放到了外面


AC代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <cstring>
#include <climits>
#include <cmath>

using namespace std;

struct edge
{
    int value;
    int left;
    int right;
    bool operator < (const edge &a) const
    {
        return value < a.value;
    }
}a[100010];

struct query
{
    int x,id;
    bool operator < (const query & a) const
    {
        return x < a.x;
    }
}b[5010];

int pre[20010],num[20010],paixu[5010];

int find(int x)
{
    return pre[x] == x ? x : pre[x] = find(pre[x]);//找老大加路径压缩
}

/*void join(int x,int y)
{
    int p = find(x);
	int q = find(y);
	if(p != q)
    {
        pre[p] = q;
        num[q] += num[p];
    }
}*/
//用上面那个join函数其实没有必要
void join(int x,int y)
{
    pre[x] = y;
    num[y] += num[x];
}

int main()
{
    int t,i,j;
    int n,m,q;
    scanf("%d",&t);
    while(t--)
    {
        int ans = 0;
        scanf("%d%d%d",&n,&m,&q);
        for(i=1; i<=n; i++)//记得是从1开始赋值
        {
            pre[i] = i;
            num[i] = 1;
        }
        for(i=0; i<m; i++)
        {
            scanf("%d%d%d",&a[i].left,&a[i].right,&a[i].value);
        }
        sort(a,a+m);
        for(i=0; i<q; i++)
        {
            scanf("%d",&b[i].x);
            b[i].id = i;
        }
        sort(b,b+q);
        j = 0;//j就刚开始赋一个0即可
        for(i=0; i<q; i++)
        {
            while(j<m && a[j].value <= b[i].x)//从上一次退出来的结果上接着搞,节省不少时间,每次从0开始搞也是错的,导致ans出错
            {
                int left = find(a[j].left);
                int right = find(a[j].right);
                j++;//j++在前,不然continue退出就不加了
                if(left == right)
                    continue;
                ans += (num[left] + num[right])*(num[left] + num[right] - 1) - num[left] * (num[left] - 1) - num[right] * (num[right] - 1);//每次利用上面的结果递推很巧妙
                join(left,right);
            }
            paixu[b[i].id] = ans;
        }
        for(i=0; i<q; i++)
        {
            printf("%d\n",paixu[i]);
        }
    }
    return 0;
}

大神地址: 点击打开链接

你可能感兴趣的:(HDU 5441 Travel(求各个集合里的秩能组合多少对,只能在一个集合里组合))