A - How Many Tables
#include<iostream> using namespace std; const int maxn = 1050; int set[maxn]; void init_set() { for(int i=0;i<=maxn;++i)set[i]=i; } int find_set(int x) { return x==set[x]?x:find_set(set[x]); } void union_set(int x,int y) { x=find_set(x); y=find_set(y); if(x!=y)set[x]=set[y]; } int main() { int t,n,m,x,y; cin>>t; while(t--) { cin>>n>>m; init_set(); for(int i=1;i<=m;++i) { cin>>x>>y; union_set(x,y); } int ans=0; for(int i=1;i<=n;++i) { if(set[i]==i)ans++; } cout<<ans<<endl; } return 0; }
优化版,降低了树高:
#include<iostream> using namespace std; const int maxn = 1050; int set[maxn],height[maxn]; void init_set() { for(int i=0;i<=maxn;++i) { set[i]=i; height[i]=0; } } int find_set(int x) { return x==set[x]?x:find_set(set[x]); } void union_set(int x,int y) { x=find_set(x); y=find_set(y); if(height[x]==height[y]) { height[x]+=1; set[y]=x; } else { if(height[x]<height[y])set[x]=y; else set[y]=x; } } int main() { int t,n,m,x,y; cin>>t; while(t--) { cin>>n>>m; init_set(); for(int i=1;i<=m;++i) { cin>>x>>y; union_set(x,y); } int ans=0; for(int i=1;i<=n;++i) { if(set[i]==i)ans++; } cout<<ans<<endl; } return 0; }
路径压缩版本:
#include<iostream> using namespace std; const int maxn = 1050; int set[maxn],height[maxn]; void init_set() { for(int i=0;i<=maxn;++i) { set[i]=i; height[i]=0; } } int find_set(int x) { if(x!=set[x])set[x]=find_set(set[x]); return set[x]; } void union_set(int x,int y) { x=find_set(x); y=find_set(y); if(height[x]==height[y]) { height[x]+=1; set[y]=x; } else { if(height[x]<height[y])set[x]=y; else set[y]=x; } } int main() { int t,n,m,x,y; cin>>t; while(t--) { cin>>n>>m; init_set(); for(int i=1;i<=m;++i) { cin>>x>>y; union_set(x,y); } int ans=0; for(int i=1;i<=n;++i) { if(set[i]==i)ans++; } cout<<ans<<endl; } return 0; }
非递归写法:
#include<iostream> using namespace std; const int maxn = 1050; int set[maxn],height[maxn]; void init_set() { for(int i=0;i<=maxn;++i) { set[i]=i; height[i]=0; } } int find_set(int x) { int r=x; while(set[r]!=r)r=set[r]; int i=x,j; while(i!=r) { j=set[i];set[i]=r;i=j; } return set[x]; } void union_set(int x,int y) { x=find_set(x); y=find_set(y); if(height[x]==height[y]) { height[x]+=1; set[y]=x; } else { if(height[x]<height[y])set[x]=y; else set[y]=x; } } int main() { int t,n,m,x,y; cin>>t; while(t--) { cin>>n>>m; init_set(); for(int i=1;i<=m;++i) { cin>>x>>y; union_set(x,y); } int ans=0; for(int i=1;i<=n;++i) { if(set[i]==i)ans++; } cout<<ans<<endl; } return 0; }
B - Ubiquitous Religions
真·套板子
#include<iostream> #define MAX 50003 using namespace std; int n,m,i,j,index; int stu[MAX]; int height[MAX]; void intial() { for(int i=1;i<=n;++i) { stu[i]=i;height[i]=0; } } int find_father(int x) { int r=x; while(stu[r]!=r)r=stu[r]; int i=x,j; while(i!=r)//路径压缩 { j=stu[i];stu[i]=r;i=j; } return stu[x]; } void union_stu(int i,int j) { i=find_father(i); j=find_father(j); if(height[i]==height[j]) { height[i]+=1; stu[j]=i; } else { if(height[i]<height[j])stu[j]=i; else stu[i]=j; } } int main() { while(cin>>n>>m&&(n||m)) { intial(); index++; for(int a=0;a<m;++a) { cin>>i>>j; union_stu(i,j); } int ans=0; for(int i=1;i<=n;++i) { if(stu[i]==i)ans++; } cout<<"Case "<<index<<": "<<ans<<endl; } return 0; }
C - The Suspects
输入变一变的套板子
#include<iostream> #define MAX 30004 using namespace std; int n,m,k,j,index; int stu[MAX]; int height[MAX]; void intial() { for(int i=0;i<n;++i) { stu[i]=i;height[i]=0; } } int find_father(int x) { int r=x; while(stu[r]!=r)r=stu[r]; int i=x,j; while(i!=r)//路径压缩 { j=stu[i];stu[i]=r;i=j; } return stu[x]; } void union_stu(int i,int j) { i=find_father(i); j=find_father(j); if(height[i]==height[j]) { height[i]+=1; stu[j]=i; } else { if(height[i]<height[j])stu[j]=i; else stu[i]=j; } } int main() { while(cin>>n>>m&&(n||m)) { intial(); for(int b=0;b<m;++b) { cin>>k;//输入这个组的总人数 if(k>1) { //先输入一个,后面的都以它为父 int father=0;cin>>father; for(int a=1;a<k;++a) { cin>>j; union_stu(j,father); } } else { int father;cin>>father; // stu[father]=father; } } int ans=1; int tag=find_father(0); for(int i=1;i<n;++i) { // cout<<"stu["<<i<<"]="<<stu[i]<<endl; if(find_father(i)==tag)ans++; } cout<<ans<<endl; } return 0; }
D - Find them, Catch them(带权并查集|种类并查集)
一开始超时,用cin读入后来改成scanf又因为考虑不周wa了,最后只好在网上找大佬的代码,原来是带权并查集,太强了,这里贴一下:https://zhuanlan.zhihu.com/p/42496208
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int N=1e5+10; int t,n,m,f[N],w[N];//w代表了当前节点与其父节点的关系 int find(int x) { if(x==f[x]) return x; int temp=f[x];//存放一下x的父亲 f[x]=find(f[x]);//找到x的祖先 w[x]=(w[x]+w[temp]+1)%2;//跟新x和他祖先的关系 return f[x];//0代表当前节点与其父节点分属不同的团伙,1代表两者来自同一团体 } void join(int x,int y) { //这里有一个隐含关系x,y不是再一个组里 int fx=find(x),fy=find(y); if(fx!=fy) { f[fx]=fy;//把x家族挂在y家族后面 w[fx]=(w[x]+w[y])%2;//跟新关系数组 } } int main() { scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { //对当前的初始状态的各个节点分析其初始状态对应的状态数。 //我们设0代表当前节点与其父节点分属不同的团伙,1代表两者来自同一团体 //初始化的时候,每个节点的根节点都来自自己,而自己与自己当然是属于同一团伙 //所以全部初始化为1。 f[i]=i;w[i]=1; } char c;int a,b; while(m--) { scanf(" %c%d%d",&c,&a,&b); if(c=='D') join(a,b); else { if(find(a)!=find(b))printf("Not sure yet. "); else if((w[a]+w[b])%2)printf("In different gangs. "); else printf("In the same gang. "); } // for(int i=0;i<n;++i) // { // cout<<"father:"<<"f["<<i<<"]="<<f[i]<<","<<"关系:"<<"w["<<i<<"]="<<w[i]<<endl; // } } } return 0; }
E - Wireless Network
这题实质上也是套并查集的模板,就是要注意分析条件:
- 首先这里的数据是不限数量的
- 用cin会导致超时
- 题目给出的d是一个范围值,相当于一个圆的半径,在圆内的点都可以互相连通
- 题目给的computer,是像一个图一样离散分布的
- 可以加进并查集的前提是他依旧被修复好了并且两点之间的距离小于等于d
#include<iostream> #include<cmath> #include<string.h> #define MAX 1005 using namespace std; int set[MAX],height[MAX]; double dir[MAX][MAX]; int d,n,m,x,y; struct a { double x; double y; int tag; }point[MAX]; void init_set() { for(int i=0;i<=MAX;++i) { set[i]=i; height[i]=0; } memset(point,0,sizeof(struct a)*MAX); memset(dir,0,sizeof(double)*MAX*MAX); } int find_set(int x) { if(x!=set[x])set[x]=find_set(set[x]); return set[x]; } void union_set(int x,int y) { x=find_set(x); y=find_set(y); if(height[x]==height[y]) { height[x]+=1; set[y]=x; } else { if(height[x]<height[y])set[x]=y; else set[y]=x; } } int main() { init_set(); scanf("%d %d",&n,&d); for(int i=1;i<=n;++i)//所有的点都从1开始编号 { // cin>>point[i].x>>point[i].y;//输入坐标 scanf("%lf%lf",&point[i].x,&point[i].y); } for(int i=1;i<=n;++i) { for(int j=i+1;j<=n;++j) { dir[i][j]=sqrt(pow(point[i].x-point[j].x,2)+pow(point[i].y-point[j].y,2)); dir[j][i]=dir[i][j]; } }//计算各个点之间的距离 char opt[MAX]={}; getchar(); while(scanf("%s",&opt)!=EOF) { if(opt[0]=='O') { int a=0;scanf("%d",&a); point[a].tag=1;//修好了标记为1 for(int i=1;i<=n;++i) { if(point[i].tag&&i!=a&&!(dir[a][i]>d)) { union_set(i,a); } } } else if(opt[0]=='S') { int a=0,b=0;scanf("%d%d",&a,&b); if(find_set(a)==find_set(b)) { cout<<"SUCCESS"<<endl; } else { cout<<"FAIL"<<endl; } } } return 0; }
F - A Bug's Life
这题跟黑帮题很像,都是种类并查集的问题,套用到模板也是同一套。
- 设定的假设都是同性恋,因为自己跟自己咳咳,所以……
- 逻辑是:如果自己跟父亲相反,父亲跟爷爷相反,则自己跟爷爷相同
- 在找同性恋的时候,如果大家都是属于一个家族的就是彼此之间存在逻辑联系那么就开始判断,如果二者都是0那么就代表存在同性恋
y#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int N=1e5+10; int t,n,m,f[N],w[N],flag=0;//w代表了当前节点与其父节点的关系 int find(int x) { if(x==f[x]) return x; int temp=f[x];//存放一下x的父亲 f[x]=find(f[x]);//路径压缩 w[x]=(w[x]+w[temp])%2;//跟新x和他祖先的关系 return f[x]; } void join(int x,int y) { int fx=find(x),fy=find(y); // cout<<">>"<<x<<" "; // for(int i=1;i<=n;++i) // { // cout<<i<<"的父亲:"<<f[i]<<",与父亲的关系:"<<w[i]<<endl; // } // cout<<endl; if(fx!=fy) { f[fx]=fy;//把x家族挂在y家族后面 w[fx]=(w[x]+w[y]+1)%2;//跟新关系数组 } else { if(w[x]==w[y])flag=1;return; } // cout<<"<< "; // for(int i=1;i<=n;++i) // { // cout<<i<<"的父亲:"<<f[i]<<",与父亲的关系:"<<w[i]<<endl; // } // cout<<endl; } int main() { scanf("%d",&t); for(int kk=1;kk<=t;++kk) { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { f[i]=i;w[i]=0;//1代表异性恋,0代表同性恋 } int a,b; flag=0; while(m--) { scanf("%d%d",&a,&b); join(a,b); // for(int i=1;i<=n;++i) // { // if(!w[i])flag=1;break; // } } printf("Scenario #%d: ",kk); if(flag) { printf("Suspicious bugs found! "); } else { printf("No suspicious bugs found! "); } printf(" "); } return 0; }
G - Cube Stacking
思路是用两个并查集,一个放爸爸,一个放儿子,但是超时了,Orz
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int N=30004; int p,n,m,f[N],S[N]; char t; int c; void Inital() { for(int i=0;i<N;++i)f[i]=i,S[i]=i; t='