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

来源:互联网 发布:短信群发软件下载 编辑:程序博客网 时间:2024/05/21 17:51

题目地址:点击打开链接

题意:每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;}

大神地址:点击打开链接

0 0