标签: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