标签:c++
题目链接:
题意:
一个犯罪团伙有N个人,他们分别有一个名字和一个网名
现已知他们会先后进出一个房间发送电报
警方可以知道所有时间下:
进出房间的人的真实名字
同时通过截获该房间发出的电报,获得网名
问最后能否将所有真实名字和虚拟网名对上
解题思路:
首先根据题目条件名字和网名是一一对应的,可以大概确定是二分匹配中的完美匹配
然而根据样例很容易看出来,要想根据正确关系来建边是很复杂的
容易的做法是:每次将不可能匹配的名字和网名建边,最后根据补图进行最大匹配即可初步得出所有匹配关系.
但现在得到的最大匹配不一定是完美匹配
要确定某个名字和网名是匹配的 我们可以删除当前已匹配的边,再进行最大匹配
如果结果减小了,则一定是对应的
这样,依次枚举每一条最大匹配中的边.即可得出答案
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<map> #define maxn 25 using namespace std; struct node { string a,b; } s[maxn]; map<string,int>m1,m2; string s1[maxn]; int edge[maxn][maxn]; int link[maxn],vis[maxn]; int mark[maxn]; int id,n; void init() { memset(edge,0,sizeof(edge)); memset(mark,0,sizeof(mark)); id=1; m1.clear(),m2.clear(); } int cmp(node x,node y) { return x.a<y.a; } int dfs(int u) { for(int i=1;i<=n;i++) { if(!vis[i]&&edge[u][i]==0) { vis[i]=1; if(link[i]==-1||dfs(link[i])) { link[i]=u; return 1; } } } return 0; } int Hungry() { memset(link,-1,sizeof(link)); int ans=0; for(int i=1;i<=n;i++) { memset(vis,0,sizeof(vis)); if(dfs(i)) ans++; } return ans; } int main() { int m,a,b,d; char ch; string str; while(~scanf("%d",&n)) { init(); for(int i=1; i<=n; i++) { cin>>s1[i]; m1[s1[i]]=i; } while(cin>>ch) { if(ch=='Q') break; else { cin>>str; if(ch=='E') { if(!m2[str]) d=m2[str]=id++; else d=m2[str]; mark[d]=1; s[d].a=str; s[d].b="???"; } else if(ch=='L') { d=m2[str]; mark[d]=0; } else if(ch=='M') { d=m1[str]; for(int i=1;i<=n;i++) if(!mark[i]) //建立反向边 edge[d][i]=1; } } } int ans=Hungry(); int linkt[maxn]; for(int i=1;i<=n;i++) //原最大匹配中的边 linkt[i]=link[i]; for(int i=1;i<=n;i++) { d=linkt[i]; edge[d][i]=1; if(Hungry()!=ans) //最大匹配减少 s[i].b=s1[d]; edge[d][i]=0; } // for(int i=1;i<=n;i++) // cout<<s[i].a<<" "<<s1[link[i]]<<endl; sort(s+1,s+1+n,cmp); for(int i=1;i<=n;i++) cout<<s[i].a<<":"<<s[i].b<<endl; } return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。
POJ1043 What's In a Name 逆图+完美匹配
标签:c++
原文地址:http://blog.csdn.net/axuan_k/article/details/47686343