电影节
Time Limit: 1000MS Memory limit: 65536K
题目描述
某届电影节评选电影,共有两部电影进入最后评选环节,有n名观众,每个人有一次投票的机会,每个人都按照规则投给其中一部电影。为了了解情况,记者随机询问了一些人,一共询问了m次,特别神奇的是,记者每次都询问两个人,而且这两个人都把票投给了同一部电影,观众编号为1~n。
输入
多组输入,每组第一行是两个整数n,m (2 <= n <=100000,0 <= m < n/2),接下来m行数据,表示m次询问,每行数据有两个整数a,b代表观众的编号(1 <= a,b <= n),观众a和观众b投票给了同一部电影,接下来一行是两个整数c,d(1 <= c,d <= n)。
输出
对于每一组输入,输出一行,如果观众c和观众d投票给同一部电影,输出”same”,如果不能确定,输出”not sure”。
示例输入
5 2
1 2
2 3
1 3
5 2
1 2
3 4
1 4
5 2
1 2
3 4
2 5
示例输出
same
not sure
not sure
<span style="font-size:18px;">#include <stdio.h> #include <stdlib.h> #include <string.h> int a[100010]; int main() { int m=0,n=0,i=0,j=0,x=0,y=0,ls=1,bj=0,jl1=0,jl2=0; while(scanf("%d",&m)!=EOF) { memset(a,0,sizeof(a)); ls=1; scanf("%d",&n); for(i=0; i<n; i++) { bj=0; scanf("%d%d",&x,&y); if(i==0) { a[x]=ls; a[y]=ls; ls++; } else { if((a[x]!=0&&a[y]==0)||(a[x]==0&&a[y]!=0)&&bj==0) { if(a[x]!=0&&a[y]==0) { a[y]=a[x]; } if(a[x]==0&&a[y]!=0) { a[x]=a[y]; } bj=1; } if(a[x]==0&&a[y]==0&&bj==0) { a[x]=ls; a[y]=ls; ls++; bj=1; } if(a[x]!=0&&a[y]!=0&&bj==0) { jl1=a[x]; jl2=a[y]; a[x]=a[y]; for(j=1;j<=(2*i+2);j++) { if(a[j]==jl1) { a[j]=jl2; } } } } } scanf("%d%d",&x,&y); if(a[x]==a[y]&&(a[x]!=0)&&(a[y]!=0)) { printf("same\n"); } else if(a[x]!=a[y]||a[x]==0||a[y]==0) { printf("not sure\n"); } } return 0; }</span>
这是一道并查集练习题,本人根据自己的想法写了一个稍微暴力的解法,AC了......
本题的思路:比如输入数据为
5 3
1 2
3 4
1 4
2 3
输出应该是same(如果得不到这个答案,那么你就再回去看题)
第一行输入数据:1 ,2,那么a[1]=1,a[2]=1;第二行数据:a[3]=2,a[4]=2;
第三行数据:(这里是重点),因为a[1]=1,a[4]=2,按照题意,这组测试数据应该是a[1],a[2],a[3],a[4]所记录的值是一样的,用那个for循环把a[1],a[2]的值都改成了2,所以,a[1]到a[4]的值就全是2了,然后判断a[2]和a[3]记录的值相等,所以就是same...通俗一点讲就是:{1,2}--->{3,4}---->{1,2,3,4}。。。。
并查集解法:
#include <stdio.h> #include <stdlib.h> #include <string.h> int s[100010]; int find(int x) { int r=x,q; while(x!=s[x]) { x=s[x]; } while(s[r]!=x) { q=s[r]; s[r]=x; r=q; } return x; } void add(int c,int d) { int fa,fb; fa=find(c); fb=find(d); if(fa!=fb) { s[fa]=fb; } } void cz(int c,int d) { int fa,fb; fa=find(c); fb=find(d); if(fa!=fb) { printf("not sure\n"); } else { printf("same\n"); } } int main() { int n,m,c,d,i; while(~scanf("%d%d",&n,&m)) { for(i=1;i<=n;i++) { s[i]=i; } while(m--) { scanf("%d%d",&c,&d); add(c,d); } scanf("%d%d",&c,&d); cz(c,d); } return 0; }
这个并查集是时间复杂度很低的算法,原理是:一开始,s[i]数组内存的是它的下标,当年你输入2 3时(2和3是连通的),s[2]=3;s[2]里面存的是和他连通的数的下标...可以打印出来试一下...其中还带了压缩路径来减少访问的时候多余的访问....
并查集原理:比如我们输入
10 5(10个点,5个输入)
1 2
2 3
3 4
2 8
8 7
1 7(询问1和7是不是连接)
答案当然是连接,没有压缩路径的代码执行后是:s[1]=2;s[2]=3;s[3]=4;s[4]=8;s[5]=5;s[6]=6;s[7]=7;s[8]=7;s[9]=9;s[10]=10;带压缩路径的输入这一组数据执行后是:s[1]=7;s[2]=7;s[3]=4;s[4]=7;s[5]=5;s[6]=6;s[7]=7;s[8]=7;s[9]=9;s[10]=10;
发现了吗,问1和8是否连通:不带压缩路径:访问s[1],根据s[1]里面的2访问s[2],然后根据s[2]里面的的数再访问,直到访问到s[7],然后程序再查询8,最后1和8都在s[7]的地方停下了,所以他们连通;带压缩路径的程序从s[1]直接访问到s[7],从s[8]直接访问到s[7],发现他们是连通的,这样看来,压缩路径的好处就很明显了....