HDU 3829 Cat VS Dog 题解

97 阅读2分钟

题目链接

思路

最后所有开心的人当中,对于任意一对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();
	}
}