POJ 1422 Air Raid 最小路径覆盖(二分图匹配)

一个PXP的有向图中,路径覆盖就是在图中找一些路径,使之覆盖了图中的所有顶点,且任何一个顶点有且只有一条路径与之关联;(如果把这些路径中的每条路径从它的起始点走到它的终点,那么恰好可以经过图中的每个顶点一次且仅一次);如果不考虑图中存在回路,那么每条路径就是一个弱连通子集.
  由上面可以得出:
  1.一个单独的顶点是一条路径;
  2.如果存在一路径p1,p2,......pk,其中p1 为起点,pk为终点,那么在覆盖图中,顶点p1,p2,......pk不再与其它的顶点之间存在有向边.
  最小路径覆盖就是找出最小的路径条数,使之成为P的一个路径覆盖.
  路径覆盖与二分图匹配的关系(必须是没有圈的有向图):
  最小路径覆盖=|P|-其中最大匹配数的求法是把P中的每个顶点pi分成两个顶点pi'与pj'',如果在p中存在一条pi到pj的边,那么在二分图P'中就有一条连接pi'与pj''的无向边;这里pi' 就是p中pi的出边,pj''就是p中pj 的一条入边;
  对于公式:最小路径覆盖=|P|-最大匹配数;可以这么来理解;
  如果匹配数为零,那么P中不存在有向边,于是显然有:
  最小路径覆盖=|P|-最大匹配数=|P|-0=|P|;即P的最小路径覆盖数为|P|;
  P'中不在于匹配边时,路径覆盖数为|P|;
  如果在P'中增加一条匹配边pi'-->pj'',那么在图P的路径覆盖中就存在一条由pi连接pj的边,也就是说pi与pj 在一条路径上,于是路径覆盖数就可以减少一个;
  如此继续增加匹配边,每增加一条,路径覆盖数就减少一条;直到匹配边不能继续增加时,路径覆盖数也不能再减少了,此时就有了前面的公式;但是这里只 是说明了每条匹配边对应于路径覆盖中的一条路径上的一条连接两个点之间的有向边;下面来说明一个路径覆盖中的每条连接两个顶点之间的有向边对应于一条匹配 边;
  与前面类似,对于路径覆盖中的每条连接两个顶点之间的每条有向边pi--->pj,我们可以在匹配图中对应做一条连接pi'与pj''的边, 显然这样做出来图的是一个匹配图(这一点用反证法很容易证明,如果得到的图不是一个匹配图,那么这个图中必定存在这样两条边 pi'---pj'' 及 pi' ----pk'',(j!=k),那么在路径覆盖图中就存在了两条边pi-->pj, pi--->pk ,那边从pi出发的路径就不止一条了,这与路径覆盖图是矛盾的;还有另外一种情况就是存在pi'---pj'',pk'---pj'',这种情况也类似可证);

  至此,就说明了匹配边与路径覆盖图中连接两顶点之间边的一一对应关系,那么也就说明了前面的公式成立!

-----------------------------------------------以上摘自百度百科-----------------------------------------------------------------------------------------------

说说我的理解吧。

题目要求是找出最少的路径覆盖所有的点,对于每个点,只属于一条路径。题目给的是有向无环图。

由于是有向图,所以这个点对于跟它关联的边,要么是起点,要么就是终点。这个起点和终点是边的起点和终点。下面所说的也都是。

并且由于只能属于一条路径,如果不是孤立点,当这个点作为起点时,必然在路径中有个边的终点对应存在,同理做终点时,必然有起点对应,那么实际就成为了终点与起点的一一对应关系,这也是为什么和二分图扯上关系的原因。于是把每个点拆成两个点,一个代表起点,一个代表终点。起点放一个集合,终点放一个集合,加边的时候,直接加入就行了。然后进行匹配,至于为什么就是顶点数-最大匹配,看一下百度百科的吧。

另外,如果有环存在,就需要进行缩点了。

/*
ID: sdj22251
PROG: subset
LANG: C++
*/
#include <iostream>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <map>
#include <string>
#include <cstring>
#include <cmath>
#include <ctime>
#define MAXN 125
#define INF 1000000000
#define eps 1e-7
using namespace std;
int n, m, e;
int head[MAXN], mark[MAXN], cx[MAXN], cy[MAXN]; //cx存储的是与左边的点,即与起点匹配的点,同理cy
struct node
{
    int v, next;
}edge[MAXN * MAXN * 2];
void insert(int x, int y)
{
    edge[e].v = y;
    edge[e].next = head[x];
    head[x] = e++;
}
void readdata()
{
    e = 0;
    int x, y;
    scanf("%d%d", &n, &m);
    memset(head, -1, sizeof(head));
    for(int i = 0; i < m; i++)
    {
        scanf("%d%d", &x, &y);
        insert(x, y);
    }
}
int path(int u)
{
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        int v = edge[i].v;
        if(!mark[v])
        {
            mark[v] = 1;
            if(cy[v] == -1 || path(cy[v]))
            {
                cx[u] = v;
                cy[v] = u;
                return 1;
            }
        }
    }
    return 0;
}
int solve()
{
    int ans = 0;
    memset(cx, -1, sizeof(cx));
    memset(cy, -1, sizeof(cy));
    for(int i = 1; i <= n; i++) //此n指的是左边的n个点,即起点
    {
        memset(mark, 0, sizeof(mark));
        ans += path(i);
    }
    return ans;
}
int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        readdata();
        printf("%d\n", n - solve());
    }
    return 0;
}

下面附上一些资料,

有向无环图的最小路径覆盖问题包括两种(设G是一个有向无环图,S是G的一个路径集合):

(1)最小点路径覆盖:满足对于G中所有的点i,i在S中的一条路径中出现,且只在S中的一条路径中出现,求S的最小容量;
(2)最小边路径覆盖:满足对于G中所有的边E,E在S中的一条路径中出现,且只在S中的一条路径中出现,求S的最小容量;

(1)最小点路径覆盖:
建立一个新图,将G中的每个点i在新图中拆成两个点i'、i'',若G中存在边<i, j>则在新图中连边<i', j''>,显然新图是一个二分图,求其最大匹配,则(N-新图最大匹配的值)就是最小点路径覆盖值。
时间复杂度:O(NM)(Hungary算法)

(2)最小边路径覆盖:
对于图中的每个点i,设D[i]为(i的入度-i的出度)的值,按照D[i]将图中的点分类:D[i]<0的称为“入少出多”的点,D[i]>0的称为“出少入多”的点,D[i]=0的称为“入出相等”的点。则有:
定理 有向无环图中最小边路径覆盖的值等于图中所有“入少出多”的点的D值之和。
证明:
其实只需证明:对于一个至少有一条边的有向无环图,必然存在一条路径,其起点是“入少出多”的点,终点是“出少入多”的点,所有中间点都是“入出相等”的点(只要不断的在图中找到并删去这条路径,直到图中无边为止)。
首先,由于图中无环,一定存在“入少出多”的点和“出少入多”的点。
然后,假设图中所有“入少出多”的点的后继(注意:后继和后代是不同的,一个点的后代包括这个点的所有后继、所有后继的后继、所有后继的后继的后继……)也都是“入少出多”的点,则图中所有“入少出多”的点构成了一个闭合子图,在这个闭合子图中,由于所有的点都是“入少出多”的,整个子图的入度之和必然大于出度之和,这是不可能的(有向图中的所有点入度之和必然等于所有点出度之和),故可得:图中必然存在至少一个“入少出多”的点,它有不是“入少出多”的后继。
这样,在这些拥有不是“入少出多”的后继的点中选择一个点i,将其作为路径的起点,在它的不是“入少出多”的后继中选出一个点j,若j是“出少入多”的点,则边<i, j>就是符合条件的一条路径,结束;若这样的j都是“入出相等”的点,则考虑j的后代:j的后继可能都是“入少出多”的,但j的后代中必然存在至少一个点j'不是“入少出多”的(否则j的所有后代也将构成全都是“入少出多”的闭合子图),这些j'中必然存在一个点的前趋i'是“入少出多”的,这是,需要舍弃原来的路径,将i'作为新路径的起点,j'作为新路径的第一个中间点,继续找;若j的后继不全是“入少出多”的,则若其中有“出少入多”的则路径已找到,若都是“入出相等”的,由于图中无环,将路径不断延伸,最终一定会找到合法路径。

由此可得,对于任何有向无环图,这样的路径都是存在的,也就证明了一开始的求最小边路径覆盖值的定理。
求有向无环图最小边路径覆盖值的时间复杂度:O(M+N)。

你可能感兴趣的:(POJ 1422 Air Raid 最小路径覆盖(二分图匹配))