UVA
来源:互联网 发布:金字塔软件免费版 编辑:程序博客网 时间:2024/06/05 11:51
题目网址点击打开链接
关于这个题,首先思路是将每个行和列分开来看,即将列和行分为两个部分,两个部分之间的边就是题目中的炸弹,连接其所在的行和列。寻找最少的点可以覆盖所有的边,即是最小点覆盖集,
在做这个题以前,首先要知道一下几点:
1增广路:
(引用)
二分图:简单来说,如果图中点可以被分为两组,并且使得所有边都跨越组的边界,则这就是一个二分图。准确地说:把一个图的顶点划分为两个不相交集
中的顶点。如果存在这样的划分,则此图为一个二分图。二分图的一个等价定义是:不含有「含奇数条边的环」的图。图 1 是一个二分图。为了清晰,我们以后都把它画成图 2 的形式。
匹配:在图论中,一个「匹配」(matching)是一个边的集合,其中任意两条边都没有公共顶点。例如,图 3、图 4 中红色的边就是图 2 的匹配。
我们定义匹配点、匹配边、未匹配点、非匹配边,它们的含义非常显然。例如图 3 中 1、4、5、7 为匹配点,其他顶点为未匹配点;1-5、4-7为匹配边,其他边为非匹配边。
最大匹配:一个图所有匹配中,所含匹配边数最多的匹配,称为这个图的最大匹配。图 4 是一个最大匹配,它包含 4 条匹配边。
增广路:从一个未匹配点出发,走交替路,如果途径另一个未匹配点(出发的点不算),则这条交替路称为增广路(agumenting path)。例如,图 5 中的一条增广路如图 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;
;;
;
;;;
- uva
- UVA
- UVA
- UVA
- uva
- UVA
- UVA
- UVA
- UVA
- UVA
- UVA
- UVA
- UVA
- UVA
- UVA
- UVA
- UVA
- UVA
- Java中的三元运算符?: error: not a statement
- HDU1403 Longest Common Substring —— 后缀数组
- 多线程多个实例
- Eclipse安装反编译工具Eclipse Class Decompiler:实现不下载源码,查看源文件
- Linux setup kafka
- UVA
- Java基础知识
- Java中为什么int、double首字母小写而String大写
- 排序算法之交换排序(Java)
- 如何压缩图片大小但不失真
- 学习笔记之浅谈面向对象编程1(对象,类)
- 爬取网易财经中股票的历史交易数据
- 台大概率第四周
- Jenkins部署到tomcat中并重启tomcat