HDU 1054 Strategic Game(二分图最小覆盖集)
http://acm.hdu.edu.cn/showproblem.php?pid=1054
题意:
给你一颗具有N个节点的树的所有边信息.现在问你最少需要多少个点放到树的节点上,使得树的任意一条边都至少有一个端点被覆盖.(其实就是最小覆盖集)
分析:
POJ题号1463.
本题明显的最小覆盖集. 且由于树天然就是二分的(可以自己验证一下,树上奇数层的顶点与偶数层的顶点构成了二分图的左右点集),所以我们只需要求该树的最小覆盖集点数即可.
我们可以用染色法把树的所有节点二分,构建二分图,然后在求该二分图的最大匹配数.
当然我们也可以直接构建翻倍的二分图,求出最大匹配数ans.最终结果等于ans/2. 本程序用的就是翻倍二分图解的. 关于翻倍二分图可见:
POJ1466:http://blog.csdn.net/u013480600/article/details/38638219.
AC代码:
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int maxn=1500+5;
struct Max_Match
{
int n;
vector<int> g[maxn];
bool vis[maxn];
int left[maxn];
void init(int n)
{
this->n=n;
for(int i=0;i<n;i++) g[i].clear();
memset(left,-1,sizeof(left));
}
bool match(int u)
{
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
if(!vis[v])
{
vis[v]=true;
if(left[v]==-1 || match(left[v]))
{
left[v]=u;
return true;
}
}
}
return false;
}
int solve()
{
int ans=0;
for(int i=0;i<n;i++)
{
memset(vis,0,sizeof(vis));
if(match(i)) ++ans;
}
return ans;
}
}MM;
int main()
{
int n;
while(scanf("%d",&n)==1)
{
MM.init(n);
for(int i=0;i<n;i++)
{
int u,num,v;
scanf("%d:(%d)",&u,&num);
while(num--)
{
scanf("%d",&v);
MM.g[u].push_back(v);
MM.g[v].push_back(u);
}
}
printf("%d\n",MM.solve()/2);
}
return 0;
}