【POJ 1151】Air Raid

【POJ 1151】Air Raid

DAG图(无回路有向图)的最小路径覆盖问题

= 最大匹配+(n-2*最大匹配)
(最大匹配*2为所有可连的点
n-2*最大匹配可求出所有独立点 再加上最大匹配即为最小所需路径)

以下引用博客:http://blog.csdn.net/whosemario/article/details/8513836

定义:

一个有向无环图,要求用尽量少的不相交的简单路径覆盖所有的节点。

构图:

建立一个二分图,把原图中的所有节点分成两份(X集合为i,Y集合为 i’),如果原来图中有i->j的有向边,则在二分图中建立i->j’的有向边。最终|最小路径覆盖|=|V|-|M|

证明:
【POJ 1151】Air Raid_第1张图片
上图中,对应左边的DAG构造了右边的二分图,可以找到二分图的一个最大匹配M:1->3’
3->4’,那么M重的这两条匹配边怎样对应DAG中的路径的边呢?
使二分图的一条边对应于DAG中的一条有向边:1->3’对应于左图的1->3,这样DAG中的1就有一个后继结点(3回事1的唯一后继结点,因为二分图中的一个顶点之多关联一条边!),所以1不会成为DAG中的一条路径中的结尾顶点,同样,3->4’对应于左图的3->4,3也不会成为结尾顶点,那么原图中总共有4个顶点,减去2个有后继的顶点,还有两个顶点,即DAG路径的结尾顶点,每个即为顶点对应一个路径。二分图中寻找最大匹配M,就是找到了对应DAG中的非路径结尾顶点的最大数目,那么DAG中|V|-|M|就是DAG中结尾顶点的最小数目,即DAG的最小路径数目。

代码如下:

#include <cstdlib>
#include <cstdio>
#include <cstring>

using namespace std;

int n;
bool vis[121],mp[121][121];
int link[121];

bool can(int p)
{
    int i;
    for(i = 1; i <= n; ++i)
    {
        if(!vis[i] && mp[p][i])
        {
            vis[i] = true;
            if(link[i] == -1 || can(link[i]))
            {
                link[i] = p;
                return 1;
            }
            vis[i] = false;
        }
    }
    return 0;
}

int main()
{
    int t,m,a,b,cnt;
    scanf("%d",&t);
    while(t--)
    {
        memset(mp,false,sizeof(mp));
        scanf("%d %d",&n,&m);
        while(m--)
        {
            scanf("%d %d",&a,&b);
            mp[a][b] = true;
        }
        memset(link,-1,sizeof(link));
        cnt = 0;
        for(int i = 1; i <= n; ++i)
        {
            memset(vis,0,sizeof(vis));
            if(can(i)) cnt++;
        }
        printf("%d\n",n-cnt);
    }
    return 0;
}

你可能感兴趣的:(最小路径覆盖)