1.团伙
题目描述
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只有一行,表示最大可能的团伙数。
输入输出样例
6 4 E 1 4 F 3 5 F 4 6 E 1 2
3
并查集和反集的思想。
即朋友是x 敌人x+n
我们可以这样理解
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,m,s,d; char ch; int f[2010]; int find(int x){ if(f[x]==x) return x; return f[x]=find(f[x]); } void unionn(int x,int y){ int x1=find(x);int y1=find(y); if(x1!=y1) f[y1]=x1; } int main(){ cin>>n; for(int i=1;i<=n*2;i++) f[i]=i; cin>>m; for(int i=1;i<=m;i++){ cin>>ch>>s>>d; if(ch=='F') unionn(s,d); else{ unionn(s,d+n); unionn(d,s+n); } } int ans=0; for(int i=1;i<=n;i++) { if(f[i]==i) ans++;//这里再解释一下吧,只有父节点是自己的“祖宗”才能代表有一个团伙,用ans来记录团伙的数量。 } cout<<ans; }
2.关押罪犯
题目描述
SS 城现有两座监狱,一共关押着 NN 名罪犯,编号分别为 1-N1−N 。他们之间的关系自然也极不和谐。很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突。我们用“怨气值”(一个正整数值)来表示某两名罪犯之间的仇恨程度,怨气值越大,则这两名罪犯之间的积怨越多。如果两名怨气值为 cc 的罪犯被关押在同一监狱,他们俩之间会发生摩擦,并造成影响力为 cc 的冲突事件。
每年年末,警察局会将本年内监狱中的所有冲突事件按影响力从大到小排成一个列表,然后上报到S 城Z 市长那里。公务繁忙的Z 市长只会去看列表中的第一个事件的影响力,如果影响很坏,他就会考虑撤换警察局长。
在详细考察了 NN 名罪犯间的矛盾关系后,警察局长觉得压力巨大。他准备将罪犯们在两座监狱内重新分配,以求产生的冲突事件影响力都较小,从而保住自己的乌纱帽。假设只要处于同一监狱内的某两个罪犯间有仇恨,那么他们一定会在每年的某个时候发生摩擦。
那么,应如何分配罪犯,才能使Z 市长看到的那个冲突事件的影响力最小?这个最小值是多少?
输入输出格式
输入格式:
每行中两个数之间用一个空格隔开。第一行为两个正整数 N,MN,M ,分别表示罪犯的数目以及存在仇恨的罪犯对数。接下来的 MM 行每行为三个正整数 a_j,b_j,c_jaj,bj,cj ,表示 a_jaj 号和 b_jbj 号罪犯之间存在仇恨,其怨气值为 c_jcj 。数据保证 1<aj≤bj≤N ,0 < cj≤ 1,000,000,0001<aj≤bj≤N,0<cj≤1,000,000,000 ,且每对罪犯组合只出现一次。
输出格式:
共 11 行,为 ZZ 市长看到的那个冲突事件的影响力。如果本年内监狱中未发生任何冲突事件,请输出 00 。
输入输出样例
说明
【输入输出样例说明】罪犯之间的怨气值如下面左图所示,右图所示为罪犯的分配方法,市长看到的冲突事件影响力是 35123512 (由 22 号和 33 号罪犯引发)。其他任何分法都不会比这个分法更优。
【数据范围】
对于 30\%30% 的数据有 N≤ 15N≤15 。
对于 70\%70% 的数据有 N≤ 2000,M≤ 50000N≤2000,M≤50000 。
对于 100\%100% 的数据有 N≤ 20000,M≤ 100000N≤20000,M≤100000 。
我们可以这么理解 要把怒气值最大的两位尽可能关在两个监狱里,然后再考虑次大的,依次类推。这样我们就可以想成 把和a怒气值最大的b b的怒气值最大的c 要是a和c的怒气值允许的话 就把a和c关在一起 所谓敌人的敌人就是朋友。
那我们要给是敌人的两个人做个标记 怎么做呢 将2倍的n初始化。设置2n个数组 x和x+n表示不同监狱 y和y+n表示不同监狱 在合并前看看x和y x+n和y+n是不是在同一个监狱里 在的话次是的影响力就是最大了啊,如果不是就合并x和y+n,y和x+n。
就是这样。
#include<iostream> #include<cmath> #include<string> #include<cstring> #include<cstdio> #include<algorithm> using namespace std; int f[100010],d[100010]; int n,m; struct dataa{ int x,y; int sum; }a[200010]; bool cmp(dataa x,dataa y){ return x.sum>y.sum; } int find(int x){ if(f[x]==x) return x; return f[x]=find(f[x]); } void unionn(int x,int y){ int x1,y1; x1=find(x),y1=find(y); if(x1!=y1) f[y1]=x1; } bool pd(int x,int y){ if(find(x)==find(y)) return true; return false; } int main(){ cin>>n>>m; for(int i=1;i<=n*2;i++) f[i]=i; for(int i=1;i<=m;i++) cin>>a[i].x>>a[i].y>>a[i].sum; sort(a+1,a+m+1,cmp); for(int i=1;i<=m;i++){ int s=a[i].x,z=a[i].y; if(pd(s,z)||pd(s+n,z+n)) { cout<<a[i].sum; return 0; } unionn(s,z+n); unionn(s+n,z); } cout<<0; }
3.食物链
题目描述
动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形。A 吃 B,B
吃 C,C 吃 A。
现有 N 个动物,以 1 - N 编号。每个动物都是 A,B,C 中的一种,但是我们并不知道
它到底是哪一种。
有人用两种说法对这 N 个动物所构成的食物链关系进行描述:
第一种说法是“1 X Y”,表示 X 和 Y 是同类。
第二种说法是“2 X Y”,表示 X 吃 Y 。
此人对 N 个动物,用上述两种说法,一句接一句地说出 K 句话,这 K 句话有的是真
的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
• 当前的话与前面的某些真的话冲突,就是假话
• 当前的话中 X 或 Y 比 N 大,就是假话
• 当前的话表示 X 吃 X,就是假话
你的任务是根据给定的 N 和 K 句话,输出假话的总数。
输入输出格式
输入格式:
从 eat.in 中输入数据
第一行两个整数,N,K,表示有 N 个动物,K 句话。
第二行开始每行一句话(按照题目要求,见样例)
输出格式:
输出到 eat.out 中
一行,一个整数,表示假话的总数。
输入输出样例
说明
1 ≤ N ≤ 5 ∗ 10^4
1 ≤ K ≤ 10^5
跟上边那两个差不多,就是有三个并查集
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> using namespace std; const int maxn=50010; int n,x,y; int f[maxn*3],k; int ch,ans; /* x存本身 x+n存猎物 x+2n存敌人 */ int find(int x){ return f[x]==x?x:f[x]=find(f[x]); } void unionn(int x,int y){ int x1=find(x),y1=find(y); if(x1!=y1) f[y1]=x1; } int main(){ cin>>n>>k; for(int i=1;i<=n*3;i++) f[i]=i; for(int i=1;i<=k;i++){ cin>>ch>>x>>y; if(x>n||y>n) { ans++; continue; }//不属于该食物链 if(ch==1){ if(find(x+n)==find(y)||find(x+2*n)==find(y)){ ans++;continue; }//x 和 y是猎物或者敌人的关系 为谎言 unionn(x,y);unionn(x+n,y+n);unionn(x+2*n,y+2*n);//如果为真 将 x 和 y 放入一个并查集中 } else if(ch==2){ if(x==y){ ans++;continue; } if(find(x)==find(y)||find(x+2*n)==find(y)){ ans++;continue; }// x是y的同类或者x是y的猎物 则是谎言 unionn(x,y+2*n);unionn(x+n,y);unionn(x+2*n,y+n);//如果 这句话是真的 那么 x的同类 吃 y ;x 吃 y的同类 ;y 吃 吃x的z。 } } cout<<ans; }