UVA

来源:互联网 发布:金字塔软件免费版 编辑:程序博客网 时间:2024/06/05 11:51

题目网址点击打开链接

关于这个题,首先思路是将每个行和列分开来看,即将列和行分为两个部分,两个部分之间的边就是题目中的炸弹,连接其所在的行和列。寻找最少的点可以覆盖所有的边,即是最小点覆盖集,

在做这个题以前,首先要知道一下几点:


1增广路:

(引用)

二分图:简单来说,如果图中点可以被分为两组,并且使得所有边都跨越组的边界,则这就是一个二分图。准确地说:把一个图的顶点划分为两个不相交集UV ,使得每一条边都分别连接UV

中的顶点。如果存在这样的划分,则此图为一个二分图。二分图的一个等价定义是:不含有「含奇数条边的环」的图。图 1 是一个二分图。为了清晰,我们以后都把它画成图 2 的形式。

匹配:在图论中,一个「匹配」(matching)是一个边的集合,其中任意两条边都没有公共顶点。例如,图 3、图 4 中红色的边就是图 2 的匹配。

Bipartite Graph(1)  Bipartite Graph(2)  Matching  Maximum Matching

我们定义匹配点、匹配边、未匹配点、非匹配边,它们的含义非常显然。例如图 3 中 1、4、5、7 为匹配点,其他顶点为未匹配点;1-5、4-7为匹配边,其他边为非匹配边。

最大匹配:一个图所有匹配中,所含匹配边数最多的匹配,称为这个图的最大匹配。图 4 是一个最大匹配,它包含 4 条匹配边。

5

增广路:从一个未匹配点出发,走交替路,如果途径另一个未匹配点(出发的点不算),则这条交替路称为增广路(agumenting path)。例如,图 5 中的一条增广路如图 6 所示(图中的匹配点均用红色标出):

6

增广路有一个重要特点:非匹配边比匹配边多一条。因此,研究增广路的意义是改进匹配。只要把增广路中的匹配边和非匹配边的身份交换即可。由于中间的匹配节点不存在其他相连的匹配边,所以这样做不会破坏匹配的性质。交换后,图中的匹配边数目比原来多了 1 条。

我们可以通过不停地找增广路来增加匹配中的匹配边和匹配点。找不到增广路时,达到最大匹配(这是增广路定理)。匈牙利算法正是这么做的。


 2  König定理:最小点覆盖数 = 最大匹配数。

关于定理的证明,借用一个博客1  点击打开链接  点击打开链接

3最后对于题目中rc的取法,

妈耶,一个一个的解释都深深叨叨的原谅我见识浅薄,

  对于一个二分图,求出其最大匹配。然后取左侧(S侧)所有的未匹配点,按照增广路算法,寻找交错路径(路径上的点都是匹配点),标记掉路径上的所有点(不存在重复标记)。那么左侧(S侧)所有的未标记点,与右侧(T侧)所有的标记点,就是实现最小点覆盖的点

以上是他们的解释,额突然发现我也描述不了,看代码其实比看说的还好懂一些,

对于总数,根据定理,直接跑一个匈牙利就可以求出答案

对于rc的答案,其实是这样的,可以这样想,你现在求r,c输出 以前已经把xy所有的可以相连的点已经连好了,但是此时还有一部分的x没有与y相连(也就是有些石头还没法被哄掉),把这一部分x拿出来,去与y相连,这样连好的y(也就是c列,就是答案的一部分),这样就可以把剩下的用列去打掉,而答案的另一部分就是原来的x(把第二步里面处理的x抠掉以后即是答案)

至于正确性,去看其他人的博客的证明:

点击打开链接   点击打开链接


就这样吧撤退,去操场,程序员爱惜身体

#include <iostream>#include <cstring>#include <string>#include <cstdio>#include<algorithm>#include<vector>#include<set>#include<map>using namespace std;const int maxn=1000+5;int s[maxn];int t[maxn];int lef[maxn];int righ[maxn];int eg[maxn][maxn];int r,c,n;void init(){    memset(eg,0,sizeof(eg));    memset(lef,0,sizeof(lef));    memset(righ,0,sizeof(righ));}int match(int x){    s[x]=1;    for(int i=1;i<=c;i++)    {        if(eg[x][i]&&!t[i])        {            t[i]=1;            if(!lef[i]||match(lef[i]))            {                lef[i]=x;                return 1;            }        }    }    return 0;}void clearst(){    memset(s,0,sizeof(s));    memset(t,0,sizeof(t));}int getans(){       int ans=0;       clearst();       for(int i=1;i<=r;i++)       {           memset(t,0,sizeof(t));           if(match(i))             ans++;       }      printf("%d",ans);}void print(){    clearst();    for(int i=1;i<=c;i++)    {        if(lef[i])        {            righ[lef[i]]=1;//去除标记的点        }    }    for(int i=1;i<=r;i++)    {        if(!righ[i])  //跑        {          match(i);        }    }    for(int i=1;i<=r;i++)        if(!s[i])        printf(" r%d",i);    for(int i=1;i<=c;i++)        if(t[i])        printf(" c%d",i);    printf("\n");}int main(){   while(cin>>r>>c>>n&&r&&c&&n)   {       init();       for(int i=0;i<n;i++)       {         int a,b;         cin>>a>>b;         eg[a][b]=1;       }   //第一步简单匈牙利   getans();    //第二步处理     print();   }    return 0;}
关于博客的引用:1点击打开链接
2;

;;

;;;


原创粉丝点击