Luogu P1195/P1892 口袋的天空/BOI团伙 【最小生成树/并查集】By cellur925
其实这俩题挺水的,团伙拿下了一血,但是感觉还是写一下博客比较好x。
一、团伙
题目描述
1920年的芝加哥,出现了一群强盗。如果两个强盗遇上了,那么他们要么是朋友,要么是敌人。而且有一点是肯定的,就是:
我朋友的朋友是我的朋友;
我敌人的敌人也是我的朋友。
两个强盗是同一团伙的条件是当且仅当他们是朋友。现在给你一些关于强盗们的信息,问你最多有多少个强盗团伙。
输入输出格式
输入格式:
输入文件gangs.in的第一行是一个整数N(2<=N<=1000),表示强盗的个数(从1编号到N)。 第二行M(1<=M<=5000),表示关于强盗的信息条数。 以下M行,每行可能是F p q或是E p q(1<=p q<=N),F表示p和q是朋友,E表示p和q是敌人。输入数据保证不会产生信息的矛盾。
输出格式:
输出文件gangs.out只有一行,表示最大可能的团伙数。
输入输出样例
输入样例#1: 复制
6 4 E 1 4 F 3 5 F 4 6 E 1 2
输出样例#1: 复制
View Code
View Code
3
普普通通的并查集维护关系题,只是需要在“敌人的敌人也是朋友”上另加处理。
对于这层关系,我们可以另加一个数组next[],输入敌人关系时,
我们记录一下,当再输入到当前敌人的敌人信息时,就可以进行合并了。
code
1 #include<cstdio> 2 #include<algorithm> 3 #include<iostream> 4 5 using namespace std; 6 7 int n,m,cnt; 8 int f[1009],next[1009]; 9 10 int getf(int x) 11 { 12 if(x==f[x]) return x; 13 else return getf(f[x]); 14 } 15 16 void merge(int x,int y) 17 { 18 int pp=getf(x); 19 int qq=getf(y); 20 if(pp!=qq) f[qq]=pp; 21 } 22 23 int main() 24 { 25 scanf("%d%d",&n,&m); 26 for(int i=1;i<=n;i++) f[i]=i; 27 for(int i=1;i<=m;i++) 28 { 29 char opt; 30 int x=0,y=0; 31 cin>>opt; 32 scanf("%d%d",&x,&y); 33 if(opt=='F') 34 merge(x,y); 35 if(opt=='E') 36 { 37 if(next[x]) merge(y,next[x]); 38 if(next[y]) merge(x,next[y]); 39 next[y]=x,next[x]=y; 40 } 41 } 42 for(int i=1;i<=n;i++) 43 if(f[i]==i) cnt++; 44 printf("%d",cnt); 45 return 0; 46 }
二、口袋的天空
我们看一看就知道这是生成树,但是与一般问题不同,这是在求k个生成树。
回顾Kruskal算法,我们可以相似地把这些边进行排序。不妨这样想:开始n个点都是独立的,他们可以近似看为n
个生成树。之后我们再一点点连边,减少生成树的数量直到k个就退出。
code
1 #include<cstdio> 2 #include<algorithm> 3 4 using namespace std; 5 typedef long long ll; 6 7 int n,m,k,x,y,z,tot,cnt; 8 ll ans; 9 int fa[10090]; 10 struct node{ 11 int f,t,v; 12 }edge[20090]; 13 14 bool cmp(node a,node b) 15 { 16 return a.v<b.v; 17 } 18 19 int getf(int x) 20 { 21 if(x==fa[x]) return x; 22 else return getf(fa[x]); 23 } 24 25 int main() 26 { 27 scanf("%d%d%d",&n,&m,&k);cnt=n; 28 for(int i=1;i<=m;i++) 29 { 30 scanf("%d%d%d",&x,&y,&z); 31 edge[++tot].f=x;edge[tot].t=y;edge[tot].v=z; 32 edge[++tot].f=y;edge[tot].t=x;edge[tot].v=z; 33 } 34 sort(edge+1,edge+tot+1,cmp); 35 for(int i=1;i<=n;i++) fa[i]=i; 36 for(int i=1;i<=tot;i++) 37 { 38 int pp=getf(edge[i].f); 39 int qq=getf(edge[i].t); 40 if(pp!=qq) 41 fa[qq]=pp,cnt--,ans+=edge[i].v; 42 if(cnt==k) break; 43 } 44 // printf("%d~~~~\n",cnt); 45 if(cnt<k) printf("No Answer"); 46 else printf("%lld",ans); 47 return 0; 48 }
独立意志与自由思想是必须争的,且须以生死力争。