POJ11422 Air Raid

一,原题链接:http://poj.org/problem?id=1422

二.题目大意:抽象成裸数学,就是给你一个有向图(无环),让你求最小路径覆盖,就是在一个图的节点上放伞兵,伞兵只能往回走,走遍所有的点需要放多少伞兵。

三,思路:如果直接知道 最小路径覆盖 = 顶点数 - 最大匹配,(这里的匹配是把图中每个点拆为2个点,如果在原图中有边,则左边到右边连一条线。

那直接套模板(逃)完了。不过这样就不好玩了是吧。

1.假设原图中一条街道都没有,伞兵要遍历所有节点,那么就需要n个伞兵。(n为节点数)

2.从U->V增加一条边,那么V上的伞兵就可以不放了。

3.这时从U->V‘再增加一条边,发现V'还是要放伞兵,因为U的伞兵已经走了。

4.再从U'->V增加一条边,发现V已经被遍历过了,因此总伞兵数不会减少。

问题转换成在原图中找使得所有顶点的入度和出度不超过1的边,每找一条边,就能减去一个兵。

如果按下面方式建图:

1.图中每个顶点拆为2个,分居二分图2边。

2,如果在原图中有边,则从左边连到右边。

这样求出来的最大匹配对应的就是原图中使得所有顶点入度和出度不超过1的边,左边对应出度,右边对应入度。

而以上推理过程,只要套上相应的数学名词,就是最小路径覆盖 = 顶点数 - 最大匹配的证明了。

四,代码

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#include <cstdlib>

using namespace std;

const int INF = 0x3f3f3f3f,
          MAX_N = 121;

class maxMatch
{
public:
    bool connected[MAX_N][MAX_N];
    bool visited[MAX_N];
    int xMatch[MAX_N], yMatch[MAX_N],
        xNum, yNum;

    maxMatch()
    {
        xNum = yNum = 0;
        memset(connected, 0, sizeof(connected));
        memset(xMatch, -1, sizeof(xMatch));
        memset(yMatch, -1, sizeof(yMatch));
    }

    bool path(int u)
    {
        int v;
        for(v = 1; v <= yNum; v++)
        if(connected[u][v] && !visited[v]){
            visited[v] = true;
            if(-1 == yMatch[v] || path(yMatch[v])){
                yMatch[v] = u;
                xMatch[u] = v;
                return true;
            }
        }
        return false;
    }

    int getRes()
    {
        int u, res = 0;
        for(u = 1; u <= xNum; u++){
            memset(visited, 0, sizeof(visited));
            if(path(u))
                res++;
        }
        return res;
    }
};

void solve()
{
    maxMatch G;
    int nodeNum, edgeNum, u, v;
    scanf("%d%d", &nodeNum, &edgeNum);
    G.xNum = G.yNum = nodeNum;

    while(edgeNum--){
        scanf("%d%d", &u, &v);
        G.connected[u][v] = true;
    }

    printf("%d\n", nodeNum-G.getRes());
}

int main()
{
    //freopen("in.txt", "r", stdin);

    int i, j, test;
    scanf("%d", &test);
    while(test--)
        solve();
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值