poj2226-Muddy Fields二分匹配 最小顶点覆盖 好题
题目
给到一个矩阵,有些格子上是草,有些是水。需要用宽度为1,长度任意的若干块木板覆盖所有的水,并不能覆盖草,木板可以交叉,但只能横竖放置,问最少要多少块板。
分析
经典的矩阵二分图构图和最小点覆盖。
无非就是两种方向,横向和竖向。我们把水块连续的编成同一号,那么对于一个点,它会有一个横向编号和纵向编号。我们要覆盖这个点,只需要覆盖这条边即可。于是问题转化成了一个最小点覆盖问题,即在二分图上选出最少的点,使它们能够覆盖所有的边。这里引出König定理。
König 定理
二分图中,最小点覆盖=最大匹配.
是不是感觉跟"最小割=最大流"有点像......
可参看最大最小定理。
证明
匈牙利算法的流程表明,一个最大匹配满足从任意一个未匹配点出发,都无法找到一条增广路径。假设最大匹配数为MM.
对于右边的所有未匹配点,我们寻找它们的所有交替路(未匹配——匹配交替出现),并把这个交替路上(包括自己)的所有点打上标记,那么最小点覆盖的点集为:左边有标记的点+右边无打标记的点,点集大小为MM.
接下来有三个问题:
- 为什么点集大小为MM?
- 为什么这个点集可以覆盖所有边?
- 为什么这个点集是最小的?
下面将按顺序证明。
- 上述点集为中每个点都是一条匹配边的某一个顶点,所以点集大小与匹配边数相等。按照上述画法,若右边一个点没有匹配过,那么它会被打上标记;若左边一个点没有匹配过,那么走不到这个点,否则将是一条新增广路。因此右边无标记点在匹配边上,左边有标记点在匹配边上。又因为不可能出现一条匹配边右边无标记而左边有标记(这种情况下左端点可以通过匹配边走到右端点给它打上标记),所以这种计算方法不会算重复,故每个点可以对应一条匹配边,点集大小为匹配边数M。
- 不存在一条边,它的左边无标记,右边有标记。一条边可以被覆盖当且仅当左右其中一个端点在点集中。只要证明不存在一条边的左右端点均不在点集中即可,即证明不存在一条边的左端点无标记,右端点有标记。若这条边是匹配边,那么右端点不可能作为上述交替路的起点,所以标记是从左端点来到,故左端点会有标记;若这条边不是匹配边,那么右端点肯定会被选作交替路起点,从而得到标记。所以不存在一条边,它的左边无标记,右边有标记。
- 一个点覆盖必须覆盖所有的匹配边,而匹配边的数量为M,没有更小的情况。由于一个点不可能连出两条匹配边(不符合匹配的定义),所以要覆盖M条匹配边,需要至少M个点。我们已经构造出了这种方案。
综上,最小点覆盖数=最大匹配数。
这类题的建图方法:
把矩阵作为一个二分图,以行列分别作为2个顶点集
首先以每一行来看,把这一行里面连续的*编号,作为一个顶点
再以每一列来看,把这一列里面连续的*编号,作为一个顶点
则每一个*都有2个编号,以行看时有一个,以列看时有一个,则把这2个编号连边,容量为1
再建一个源点,连接所有行的编号,一个汇点,连接所有列的编号
这道题要求的是,所有*都被覆盖,即找到一个顶点的集合S,使得任意边都有至少一个顶点属于
S,即求一个点集顶点覆盖S,又要木板数最少,所以求的就是最小顶点覆盖。
最小顶点覆盖怎么求?
二分图中,有:
最小顶点覆盖=最大匹配
所以这道题就转化为求二分图的最大匹配了
再转化为最大流dinic算法。
dinic算法
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue> using namespace std; const int maxn=;
const int inf=0x3f3f3f3f;
const int s=;
int t;
int tota;
int totb; inline int min(int x,int y)
{
return x<y?x:y;
} struct Edge
{
int to,cap,rev;
};
vector<Edge>edge[maxn];
int iter[maxn];
int level[maxn];
char str[][];
int hash[][]; void addedge(int from,int to,int cap)
{
edge[from].push_back((Edge){to,cap,edge[to].size()});
edge[to].push_back((Edge){from,,edge[from].size()-});
} void build_graph(int n,int m)
{
for(int i=;i<n*m;i++)
edge[i].clear();
tota=;
for(int i=;i<=n;i++)
{
int j=;
while(j<=m)
{
if(str[i][j]=='*')
{
tota++;
hash[i][j]=tota;
while(j<=m&&str[i][j+]=='*')
{
j++;
hash[i][j]=tota;
}
}
j++;
}
}
totb=tota;
for(int j=;j<=m;j++)
{
int i=;
while(i<=n)
{
if(str[i][j]=='*')
{
totb++;
addedge(hash[i][j],totb,);
while(i<=n&&str[i+][j]=='*')
{
i++;
addedge(hash[i][j],totb,);
}
}
i++;
}
}
t=tota+totb+;
for(int i=;i<=tota;i++)
addedge(s,i,);
for(int i=tota+;i<=totb;i++)
addedge(i,t,);
} void bfs()
{
memset(level,-,sizeof level);
queue<int>que;
while(!que.empty())
que.pop();
que.push(s);
level[s]=;
while(!que.empty())
{
int u=que.front();
que.pop();
for(int i=;i<edge[u].size();i++)
{
Edge &e=edge[u][i];
if(e.cap>&&level[e.to]<)
{
level[e.to]=level[u]+;
que.push(e.to);
}
}
}
} int dfs(int u,int f)
{
if(u==t)
return f;
for(int &i=iter[u];i<edge[u].size();i++)
{
Edge &e=edge[u][i];
if(e.cap>&&level[e.to]>level[u])
{
int d=dfs(e.to,min(f,e.cap));
if(d>)
{
e.cap-=d;
edge[e.to][e.rev].cap+=d;
return d;
} }
}
return ;
} int solve()
{
int flow=;
while(true)
{
bfs();
if(level[t]<)
return flow;
memset(iter,,sizeof iter);
int f;
while(f=dfs(s,inf))
{
flow+=f;
}
}
} int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
for(int i=;i<=n;i++)
{
scanf("%s",str[i]+);
}
build_graph(n,m);
printf("%d\n",solve());
}
return ;
}
匈牙利算法
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn=;
const int maxc=1e3+;
bool f[maxc][maxc],alr[maxc];
int match[maxc],row[maxn][maxn],col[maxn][maxn],lid=,rid=,n,m,ans=;
char s[maxn][maxn];
bool dfs(int x) {
for (int i=;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() { scanf("%d%d",&n,&m);
for (int i=;i<=n;++i) scanf("%s",s[i]+);
for (int i=;i<=n;++i) for (int j=;j<=m;++j) if (s[i][j]=='*') {
++lid;
while (j<=m && s[i][j]=='*') row[i][j++]=lid;
--j;
}
for (int j=;j<=m;++j) for (int i=;i<=n;++i) if (s[i][j]=='*') {
++rid;
while (i<=n && s[i][j]=='*') col[i++][j]=rid;
--i;
}
for (int i=;i<=n;++i) for (int j=;j<=m;++j) if (s[i][j]=='*') f[row[i][j]][col[i][j]]=true;
// for(int i = 1;i <= n; i ++)for(int j = 1;j <= m;j ++){
// cout << f[row[i][j]][col[i][j]] << " " ;
// }
for (int i=;i<=lid;++i) memset(alr,,sizeof alr),ans+=dfs(i);
printf("%d\n",ans);
}
poj2226-Muddy Fields二分匹配 最小顶点覆盖 好题的更多相关文章
- POJ2226 Muddy Fields 二分匹配 最小顶点覆盖 好题
在一个n*m的草地上,.代表草地,*代表水,现在要用宽度为1,长度不限的木板盖住水, 木板可以重叠,但是所有的草地都不能被木板覆盖. 问至少需要的木板数. 这类题的建图方法: 把矩阵作为一个二分图,以 ...
- poj 2226 Muddy Fields (二分匹配)
Muddy Fields Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 7340 Accepted: 2715 Desc ...
- POJ 1498[二分匹配——最小顶点覆盖]
题目链接:[http://acm.hdu.edu.cn/showproblem.php?pid=1498] 题意:给出一个大小为n*n(0<n<100)的矩阵,矩阵中放入m种颜色(标号为1 ...
- [USACO2005][POJ2226]Muddy Fields(二分图最小点覆盖)
题目:http://poj.org/problem?id=2226 题意:给你一个字符矩阵,每个位置只能有"*"或者“.",连续的横着或者竖的“*"可以用一块木 ...
- POJ2226 Muddy Fields(二分图最小点覆盖集)
题目给张R×C的地图,地图上*表示泥地..表示草地,问最少要几块宽1长任意木板才能盖住所有泥地,木板可以重合但不能盖住草地. 把所有行和列连续的泥地(可以放一块木板铺满的)看作点且行和列连续泥地分别作 ...
- nyoj 237 游戏高手的烦恼 二分匹配--最小点覆盖
题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=237 二分匹配--最小点覆盖模板题 Tips:用邻接矩阵超时,用数组模拟邻接表WA,暂时只 ...
- poj2226 Muddy Fields 填充棒子(二分匹配)
参考博客:https://blog.csdn.net/liujc_/article/details/51287019 参考博客:https://blog.csdn.net/acdreamers/art ...
- UVA 11419 SAM I AM(最大二分匹配&最小点覆盖:König定理)
题意:在方格图上打小怪,每次可以清除一整行或一整列的小怪,问最少的步数是多少,又应该在哪些位置操作(对输出顺序没有要求). 分析:最小覆盖问题 这是一种在方格图上建立的模型:令S集表示“行”,T集表示 ...
- HDU 3861 The King’s Problem(tarjan缩点+最小路径覆盖:sig-最大二分匹配数,经典题)
The King’s Problem Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Other ...
随机推荐
- websocket聊天体验
light-example-4j/websocket目录有client-to-server.peer-to-peer两个示例项目,解决了我的两个问题:在线聊天.日志查看. 在线聊天,后续可以支持:最近 ...
- 黑马vue---13、事件修饰符的介绍
黑马vue---13.事件修饰符的介绍 一.总结 一句话总结: .stop 阻止冒泡:input type="button" value="戳他" @click ...
- Fiddler抓包原理和使用详解
iddler抓包工具在APP开发过程中使用非常频繁,对开发者理解HTTP网络传输原理以及分析定位网络方面的问题非常有帮助.今天抽点时间出来总结一下Fiddler在实际开发过程中的应用. 我开发过程中使 ...
- ADS1.2与MDK4.7冲突问题的解决方法
需要添加2个系统变量. 1.添加环境变量名: ARMCC5LIB 变量值:C:\Keil\ARM\ARMCC\lib(实际Keil安装目录) 2.添加环境变量:ARMCC5INC变量值:C:\Keil ...
- 八十二:memcached之python操作memcached
安装:pip install python-memcached 创建链接:mc = memcache.Client(['127.0.0.1:11211'], debug=True) 插入数据:mc.s ...
- shell脚本安装python、pip--这种写法是错误的---每一个命令执行完都要判断是否执行成功,否则无法进行下一步
shell脚本安装python.pip--不需要选择安装项目--不管用总报错,必须带上判断符号,while没有这种用法,写在这里为了以后少走弯路,所以不要用下面的执行了 首先把pip-.tgz 安装包 ...
- 嵌入式【杂记--手机芯片与pc】
手机.身边的移动设备大多数是嵌入式计算机,pc也是计算机,只是功耗上很大. 手机所采用的大多数芯片是英国ARM公司的架构coretom A系列 core, Intel公司采用自己的架构设计的芯片适用于 ...
- java base64相关
文件转Base64: public static String imgToBase64(InputStream inStream) { byte[] data = null; try { //avai ...
- 【ABAP系列】【第六篇】SAP ABAP7.50 之隐式增强
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[ABAP系列][第六篇]SAP ABAP7.5 ...
- 【AMADM】django-braces -- Django的一些可重用的,通用型的mixin
动机 简介 个人评分 动机 Django1.8以后加入了Class Based View,以及GenericView概念.之后在django对类的使用更加频繁. 而一些特性,相信我们都有重复地编写过. ...