您的位置:首页 > 其它

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

2015-09-14 10:45 447 查看
题目地址:点击打开链接

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


大神地址:点击打开链接
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: