思路
最后所有开心的人当中,对于任意一对a,b,必定满足a喜欢的不是b讨厌的且b喜欢的不是a讨厌的。如果在喜欢和讨厌矛盾的人之间连边,就可以转化为最小点覆盖问题。要想开心的人最多,就要用最少的点消除掉所有的矛盾边。每消掉一条矛盾边得到的最优结果必定会产生一人开心一人不开心,矛盾连边跑匈牙利算法即可。
根据题意可知连边后的图必定为二分图,但是所有的节点都混在一起,不能正常跑匈牙利算法。因此每匹配一条边就分别维护两个端点的匹配数组。在枚举节点dfs时如果一个节点已经匹配完成就continue。
代码
#include<iostream>
#include<map>
#include<cstring>
#define rep(i,st,ed) for(int i=st;i<=ed;++i)
using namespace std;
const int N=505;
int n,m,p,cnt,tot,ind,ans;
int head[N],vis[N],a[N],b[N],pp[N];
map<string,int> ma;
struct Edge{
int nxt,to;
}e[N*N];
inline void add_edge(int u,int v,int flag)
{
e[++tot].nxt=head[u];e[tot].to=v;head[u]=tot;
if(flag)
add_edge(v,u,0);
}
int dfs(int u)
{
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(vis[v]==ind)
continue;
vis[v]=ind;
if(!pp[v] || dfs(pp[v]))
{
pp[v]=u;
pp[u]=v;
return 1;
}
}
return 0;
}
int main()
{
while(~scanf("%d%d%d",&n,&m,&p))
{
memset(head,0,sizeof(head));
memset(vis,0,sizeof(vis));
memset(pp,0,sizeof(pp));
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(e,0,sizeof(e));
ma.clear();
ans=ind=cnt=tot=0;
rep(i,1,p)
{
string like,dis;
cin>>like>>dis;
if(ma[like])
a[i]=ma[like];
else
{
ma[like]=++cnt;
a[i]=cnt;
}
if(ma[dis])
b[i]=ma[dis];
else
{
ma[dis]=++cnt;
b[i]=cnt;
}
}
rep(i,1,p)
{
rep(j,i+1,p)
{
if(a[i]==b[j] || a[j]==b[i])
{
add_edge(i,j,1);
}
}
}
rep(i,1,p)
{
if(pp[i])
continue;
++ind;
ans+=dfs(i);
}
printf("%d\n",p-ans);
getchar();
}
}