HDU1054Strategic Game(最小顶点覆盖数)

来源:互联网 发布:婚礼发布网站源码java 编辑:程序博客网 时间:2024/06/08 05:08

我们来先了解一下什么是最小顶点覆盖;

G顶点覆盖是一个顶点集合V,使得G中的每一条边都接触V中的至少一个顶点。我们称集合V覆盖了G的边。最小顶点覆盖是用最少的顶点来覆盖所有的边。顶点覆盖数\tau是最小顶点覆盖的大小。

相应地,图G边覆盖是一个边集合E,使得G中的每一个顶点都接触E中的至少一条边。

如果只说覆盖,则通常是指顶点覆盖,而不是边覆盖。

在二分图中  :最大匹配数=最小顶点覆盖数;

求二分图最大匹配可以用最大流(Maximal Flow)或者匈牙利算法(Hungarian Algorithm)



题意:鲍勃喜欢玩电脑游戏,特别是战略游戏,但有时他无法找到解决方案,速度不够快,那么他很伤心。现在,他有以下的问题。他必须捍卫一个中世纪的城市,形成了树的道路。他需要把最少数量的战士放在节点上,使他们可以观察所有的边(每个战士可以观察多条边)。你能帮助他吗?士兵,鲍勃把一个给定的树,你的程序应该计算最小的战士数量。输入文件包含多个数据集的文本格式。



分析:

 首先来说第一种解法,即最小顶点覆盖问题。由nig定理定理可知,二分图的最小顶点覆盖数等于二分图的最大匹配数。

关于nig定理的证明网上也比较多。大家可以百度找一找。题目中的这棵树之所以可以当成二分图,是因为如果从一个点出发,那么可以将整棵树分成奇数点层和偶数点层。由于树是一种特殊的图。n个点由(n-1)条边连接起来。这样假定一个点为树的根,假设各点间的边权值为1。那么从树根出发遍历整棵树,根据各点到根的路径的奇偶性即可将所有点分成两个集合。奇数点与偶数点交替出现。假设奇数点与偶数点连边,偶数点则继续和下一层的奇数点连边。这就与二分图中同类集合点间无边,不同类集合点间有边相连吻合起来了。所以满足二分图的性质。也可以用二分图最大匹配进行求解。这个对点分成奇数点偶数点的方法与搜索剪枝中的奇偶剪枝很像。奇偶剪枝中对点的分类与该方法相同。


下面继续说二分图最大匹配。由于对该二分图进行了补全(无向图),边增加为原来边的二倍。所以最终结果要除以2。

      二分图最小顶点覆盖=双向二分图最大匹配/2

#include<iostream>#include<cstring>#include<cstdio>#include<vector>using namespace std;const int maxn = 1505;bool vis[maxn];//标记节点是否被访问过int Left[maxn];//标记n个节点的增广节点的编号vector<int> mp[maxn];bool match(int cur){    for(int i = 0; i < mp[cur].size(); i++)    {        int v = mp[cur][i];        if(!vis[v])//若v与cur相邻,且没有被标记        {            vis[v] = 1;            if(Left[v] == -1 || match(Left[v]))//如果v未在前一个匹配M中,或者,v在匹配M中,但从v相邻的节点出发可以找到增广路            {                Left[v] = cur;//则把cur放到匹配M中                return true;            }        }    }    return false;}int main(){    int n, u, v, num;    while(scanf("%d", &n) != EOF)    {        for(int i = 0; i < n; i++)            mp[i].clear();        for(int i = 0; i < n; i++)        {            scanf("%d:(%d)", &u, &num);            while(num--)            {                scanf("%d", &v);                mp[u].push_back(v);                mp[v].push_back(u);            }        }        int ans = 0;        memset(Left, -1, sizeof(Left));        for(int i = 0; i < n; i++)        {            memset(vis, false, sizeof(vis));            if(match(i))                ans++;//若有增广路,匹配数则加一        }        printf("%d\n", ans / 2);//最小顶点覆盖 == 最大匹配(双向图)/2;    }    return 0;}



原创粉丝点击