Code Names(二分图)
牛客题目链接:https://ac.nowcoder.com/acm/contest/12606/B
训练赛第一场,这道题看了半天CY想出是二分图,我也把二分图模板写出来套进去,结果样例都没过,再想想就直接把二分图否定了,结果是因为无向图找的最大匹配需要除以2。。。(太离谱了)。
然后就往图论里的最大团那里去像,一顿T,真的是崩溃,要是坚持一下仔细读一读题可能就能过了,赛后CY直呼可惜(●'◡'●)。
因为N<=500,所以时间复杂度O(n3)也不会超时。先将输入的字符串用两个循环建立之间是否存在关系,若字符串只有两个字符不同就定义为有关系,可将关系存在二维数组edge里,即第i个数组和第j个数组有关系就edge[i][j]=1,edge[j][i]=1;
顺带几个定义。
二分图的最大独立集:选出一些顶点使得这些顶点两两不相邻,则这些点构成的集合称为独立集。找出一个包含顶点数最多的独立集称为最大独立集。
二分图的最小顶点覆盖:假如选了一个点就相当于覆盖了以它为端点的所有边。最小顶点覆盖就是选择最少的点来覆盖所有的边。
二分图的最大团:对于一般图来说,团是一个顶点集合,且由该顶点集合诱导的子图是一个完全图,简单说,就是选出一些顶点,这些顶点两两之间都有边。最大团就是使得选出的这个顶点集合最大。对于二分图来说,我们默认为左边的所有点之间都有边,右边的所有顶点之间都有边。那么,实际上,我们是要在左边找到一个顶点子集X,在右边找到一个顶点子集Y,使得X中每个顶点和Y中每个顶点之间都有边。
最小顶点覆盖 = 二分图的最大匹配
最大独立集 = 所有顶点数 - 最小顶点覆盖
二分图的最大团 = 补图的最大独立集(补图:在原图有边,补图就变为无边,否则就是有边,直接反过来)
(因为原图最大独立集是两两没关系,所以反过来补图的最大顶立集就是两两有关系了)(๑•̀ㅂ•́)و✧
根据题目要求,需要找出最多的互相无关系的字符串,就相当于找出最大独立集,那么可以采用二分图匈牙利算法求出最大匹配,然后就可以算出最大独立集。
注意:题目给出的是无向图,所以求出的最大匹配将所有用到的点用了两遍,(1->2连成边,那么2->1也会形成一条边)所以 最小顶点覆盖=最大匹配/2(大坑)
附上代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N=505; 4 int edge[N][N],vis[N],cy[N],n; 5 int path(int u)//匈牙利算法 6 { 7 for(int v=1;v<=n;v++) 8 { 9 if(edge[u][v]&&!vis[v])//有边且未遍历过 10 { 11 vis[v]=1;//标记 12 if(!cy[v]||path(cy[v]))//找到增广路 13 { 14 cy[v]=u;//匹配 15 return 1; 16 } 17 } 18 } 19 return 0; 20 } 21 22 int main(void) 23 { 24 char s[N][N]; 25 cin>>n; 26 memset(cy,0,sizeof(cy)); 27 memset(edge,0,sizeof(edge)); 28 for(int i=1;i<=n;i++) 29 cin>>(s[i]+1); 30 int len=strlen(s[1]+1); 31 for(int i=1;i<=n;i++) 32 { 33 for(int j=1;j<i;j++) 34 { 35 int ans=0; 36 for(int k=1;k<=len;k++) 37 { 38 if(s[i][k]!=s[j][k]) 39 ans++; 40 } 41 if(ans==2) 42 { 43 edge[i][j]=1;//形成关系 44 edge[j][i]=1; 45 } 46 } 47 } 48 int sum=0;//最大匹配 49 for(int i=1;i<=n;i++) 50 { 51 memset(vis,0,sizeof(vis)); 52 sum+=path(i); 53 } 54 cout<<n-sum/2<<endl; 55 return 0; 56 }
转自:https://i.cnblogs.com/posts/edit;postId=14496134