• poj2226-Muddy Fields


    题目

    给到一个矩阵,有些格子上是草,有些是水。需要用宽度为1,长度任意的若干块木板覆盖所有的水,并不能覆盖草,木板可以交叉,但只能横竖放置,问最少要多少块板。

    分析

    经典的矩阵二分图构图和最小点覆盖。
    无非就是两种方向,横向和竖向。我们把水块连续的编成同一号,那么对于一个点,它会有一个横向编号和纵向编号。我们要覆盖这个点,只需要覆盖这条边即可。于是问题转化成了一个最小点覆盖问题,即在二分图上选出最少的点,使它们能够覆盖所有的边。这里引出König定理。

    König 定理

    二分图中,最小点覆盖=最大匹配.

    是不是感觉跟"最小割=最大流"有点像......
    可参看最大最小定理

    证明

    匈牙利算法的流程表明,一个最大匹配满足从任意一个未匹配点出发,都无法找到一条增广路径。假设最大匹配数为(M).
    对于右边的所有未匹配点,我们寻找它们的所有交替路(未匹配——匹配交替出现),并把这个交替路上(包括自己)的所有点打上标记,那么最小点覆盖的点集为:左边有标记的点+右边无打标记的点,点集大小为(M).
    接下来有三个问题:

    • 为什么点集大小为(M)
    • 为什么这个点集可以覆盖所有边?
    • 为什么这个点集是最小的?

    下面将按顺序证明。

    • 上述点集为中每个点都是一条匹配边的某一个顶点,所以点集大小与匹配边数相等。按照上述画法,若右边一个点没有匹配过,那么它会被打上标记;若左边一个点没有匹配过,那么走不到这个点,否则将是一条新增广路。因此右边无标记点在匹配边上,左边有标记点在匹配边上。又因为不可能出现一条匹配边右边无标记而左边有标记(这种情况下左端点可以通过匹配边走到右端点给它打上标记),所以这种计算方法不会算重复,故每个点可以对应一条匹配边,点集大小为匹配边数(M)
    • 不存在一条边,它的左边无标记,右边有标记。一条边可以被覆盖当且仅当左右其中一个端点在点集中。只要证明不存在一条边的左右端点均不在点集中即可,即证明不存在一条边的左端点无标记,右端点有标记。若这条边是匹配边,那么右端点不可能作为上述交替路的起点,所以标记是从左端点来到,故左端点会有标记;若这条边不是匹配边,那么右端点肯定会被选作交替路起点,从而得到标记。所以不存在一条边,它的左边无标记,右边有标记。
    • 一个点覆盖必须覆盖所有的匹配边,而匹配边的数量为(M),没有更小的情况。由于一个点不可能连出两条匹配边(不符合匹配的定义),所以要覆盖(M)条匹配边,需要至少(M)个点。我们已经构造出了这种方案。

    综上,最小点覆盖数=最大匹配数。

    代码

    写的时候WA了几次,主要是没有算好,虽然矩阵的大小是(50*50),但是建出来的图可能有(625*2)个点(最大矩阵插空排列),所以空间开小了。第二就是匈牙利算法在写的时候出了点错误:下面遍历左边交替路起点的时候不需要判断是否已经匹配,因为可能找到更好的方案,还有(match[i])表示的是右边的(i)对左边的点的匹配,所以赋值应是(match[i]=x).

    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int maxn=55;
    const int maxc=1e3+10;
    bool f[maxc][maxc],alr[maxc];
    int match[maxc],row[maxn][maxn],col[maxn][maxn],lid=0,rid=0,n,m,ans=0;
    char s[maxn][maxn];
    bool dfs(int x) {
    	for (int i=1;i<=rid;++i) if (!alr[i] && f[x][i]) {
    		alr[i]=true;
    		if (!match[i] || dfs(match[i])) {
    			match[i]=x;
    			return true;
    		}
    	}
    	return false;
    }
    int main() {
    	#ifndef ONLINE_JUDGE
    	freopen("test.in","r",stdin);
    	#endif
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=n;++i) scanf("%s",s[i]+1);
    	for (int i=1;i<=n;++i) for (int j=1;j<=m;++j) if (s[i][j]=='*') {
    		++lid;
    		while (j<=m && s[i][j]=='*') row[i][j++]=lid;
    		--j;
    	}
    	for (int j=1;j<=m;++j) for (int i=1;i<=n;++i) if (s[i][j]=='*') {
    		++rid;
    		while (i<=n && s[i][j]=='*') col[i++][j]=rid;
    		--i;
    	}
    	for (int i=1;i<=n;++i) for (int j=1;j<=m;++j) if (s[i][j]=='*') f[row[i][j]][col[i][j]]=true;
    	for (int i=1;i<=lid;++i) memset(alr,0,sizeof alr),ans+=dfs(i);
    	printf("%d
    ",ans);
    }
    
  • 相关阅读:
    R vs Python:构建data.frame、读取csv与统计描述
    R语言学习笔记:使用reshape2包实现整合与重构
    Python学习笔记:lambda表达式
    Python学习笔记:startswith & endswith 判断开头结尾是否为指定字符串
    Python学习笔记:一手漂亮的Python函数
    电信行业数据挖掘分析
    Oracle学习笔记:实现select top N的方法
    Oracle学习笔记:ORA-22992 cannot use LOB locators selected from remote tables
    Linux学习笔记:ls和ll命令
    vb常用命名空间
  • 原文地址:https://www.cnblogs.com/owenyu/p/6724698.html
Copyright © 2020-2023  润新知