HDU1054Strategic Game(最小顶点覆盖数)

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

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;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值