题目

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

分析

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

König 定理

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

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

证明

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

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

下面将按顺序证明。

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

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

这类题的建图方法:

把矩阵作为一个二分图,以行列分别作为2个顶点集

首先以每一行来看,把这一行里面连续的*编号,作为一个顶点

再以每一列来看,把这一列里面连续的*编号,作为一个顶点

则每一个*都有2个编号,以行看时有一个,以列看时有一个,则把这2个编号连边,容量为1

再建一个源点,连接所有行的编号,一个汇点,连接所有列的编号

这道题要求的是,所有*都被覆盖,即找到一个顶点的集合S,使得任意边都有至少一个顶点属于

S,即求一个点集顶点覆盖S,又要木板数最少,所以求的就是最小顶点覆盖。

最小顶点覆盖怎么求?

二分图中,有:

最小顶点覆盖=最大匹配

所以这道题就转化为求二分图的最大匹配了

再转化为最大流dinic算法。

dinic算法

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<vector>
  4. #include<queue>
  5.  
  6. using namespace std;
  7.  
  8. const int maxn=;
  9. const int inf=0x3f3f3f3f;
  10. const int s=;
  11. int t;
  12. int tota;
  13. int totb;
  14.  
  15. inline int min(int x,int y)
  16. {
  17. return x<y?x:y;
  18. }
  19.  
  20. struct Edge
  21. {
  22. int to,cap,rev;
  23. };
  24. vector<Edge>edge[maxn];
  25. int iter[maxn];
  26. int level[maxn];
  27. char str[][];
  28. int hash[][];
  29.  
  30. void addedge(int from,int to,int cap)
  31. {
  32. edge[from].push_back((Edge){to,cap,edge[to].size()});
  33. edge[to].push_back((Edge){from,,edge[from].size()-});
  34. }
  35.  
  36. void build_graph(int n,int m)
  37. {
  38. for(int i=;i<n*m;i++)
  39. edge[i].clear();
  40. tota=;
  41. for(int i=;i<=n;i++)
  42. {
  43. int j=;
  44. while(j<=m)
  45. {
  46. if(str[i][j]=='*')
  47. {
  48. tota++;
  49. hash[i][j]=tota;
  50. while(j<=m&&str[i][j+]=='*')
  51. {
  52. j++;
  53. hash[i][j]=tota;
  54. }
  55. }
  56. j++;
  57. }
  58. }
  59. totb=tota;
  60. for(int j=;j<=m;j++)
  61. {
  62. int i=;
  63. while(i<=n)
  64. {
  65. if(str[i][j]=='*')
  66. {
  67. totb++;
  68. addedge(hash[i][j],totb,);
  69. while(i<=n&&str[i+][j]=='*')
  70. {
  71. i++;
  72. addedge(hash[i][j],totb,);
  73. }
  74. }
  75. i++;
  76. }
  77. }
  78. t=tota+totb+;
  79. for(int i=;i<=tota;i++)
  80. addedge(s,i,);
  81. for(int i=tota+;i<=totb;i++)
  82. addedge(i,t,);
  83. }
  84.  
  85. void bfs()
  86. {
  87. memset(level,-,sizeof level);
  88. queue<int>que;
  89. while(!que.empty())
  90. que.pop();
  91. que.push(s);
  92. level[s]=;
  93. while(!que.empty())
  94. {
  95. int u=que.front();
  96. que.pop();
  97. for(int i=;i<edge[u].size();i++)
  98. {
  99. Edge &e=edge[u][i];
  100. if(e.cap>&&level[e.to]<)
  101. {
  102. level[e.to]=level[u]+;
  103. que.push(e.to);
  104. }
  105. }
  106. }
  107. }
  108.  
  109. int dfs(int u,int f)
  110. {
  111. if(u==t)
  112. return f;
  113. for(int &i=iter[u];i<edge[u].size();i++)
  114. {
  115. Edge &e=edge[u][i];
  116. if(e.cap>&&level[e.to]>level[u])
  117. {
  118. int d=dfs(e.to,min(f,e.cap));
  119. if(d>)
  120. {
  121. e.cap-=d;
  122. edge[e.to][e.rev].cap+=d;
  123. return d;
  124. }
  125.  
  126. }
  127. }
  128. return ;
  129. }
  130.  
  131. int solve()
  132. {
  133. int flow=;
  134. while(true)
  135. {
  136. bfs();
  137. if(level[t]<)
  138. return flow;
  139. memset(iter,,sizeof iter);
  140. int f;
  141. while(f=dfs(s,inf))
  142. {
  143. flow+=f;
  144. }
  145. }
  146. }
  147.  
  148. int main()
  149. {
  150. int n,m;
  151. while(~scanf("%d%d",&n,&m))
  152. {
  153. for(int i=;i<=n;i++)
  154. {
  155. scanf("%s",str[i]+);
  156. }
  157. build_graph(n,m);
  158. printf("%d\n",solve());
  159. }
  160. return ;
  161. }

匈牙利算法

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<iostream>
  4. using namespace std;
  5. const int maxn=;
  6. const int maxc=1e3+;
  7. bool f[maxc][maxc],alr[maxc];
  8. int match[maxc],row[maxn][maxn],col[maxn][maxn],lid=,rid=,n,m,ans=;
  9. char s[maxn][maxn];
  10. bool dfs(int x) {
  11. for (int i=;i<=rid;++i) if (!alr[i] && f[x][i]) {
  12. alr[i]=true;
  13. if (!match[i] || dfs(match[i])) {
  14. match[i]=x;
  15. return true;
  16. }
  17. }
  18. return false;
  19. }
  20. int main() {
  21.  
  22. scanf("%d%d",&n,&m);
  23. for (int i=;i<=n;++i) scanf("%s",s[i]+);
  24. for (int i=;i<=n;++i) for (int j=;j<=m;++j) if (s[i][j]=='*') {
  25. ++lid;
  26. while (j<=m && s[i][j]=='*') row[i][j++]=lid;
  27. --j;
  28. }
  29. for (int j=;j<=m;++j) for (int i=;i<=n;++i) if (s[i][j]=='*') {
  30. ++rid;
  31. while (i<=n && s[i][j]=='*') col[i++][j]=rid;
  32. --i;
  33. }
  34. for (int i=;i<=n;++i) for (int j=;j<=m;++j) if (s[i][j]=='*') f[row[i][j]][col[i][j]]=true;
  35. // for(int i = 1;i <= n; i ++)for(int j = 1;j <= m;j ++){
  36. // cout << f[row[i][j]][col[i][j]] << " " ;
  37. // }
  38. for (int i=;i<=lid;++i) memset(alr,,sizeof alr),ans+=dfs(i);
  39. printf("%d\n",ans);
  40. }

poj2226-Muddy Fields二分匹配 最小顶点覆盖 好题的更多相关文章

  1. POJ2226 Muddy Fields 二分匹配 最小顶点覆盖 好题

    在一个n*m的草地上,.代表草地,*代表水,现在要用宽度为1,长度不限的木板盖住水, 木板可以重叠,但是所有的草地都不能被木板覆盖. 问至少需要的木板数. 这类题的建图方法: 把矩阵作为一个二分图,以 ...

  2. poj 2226 Muddy Fields (二分匹配)

    Muddy Fields Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 7340   Accepted: 2715 Desc ...

  3. POJ 1498[二分匹配——最小顶点覆盖]

    题目链接:[http://acm.hdu.edu.cn/showproblem.php?pid=1498] 题意:给出一个大小为n*n(0<n<100)的矩阵,矩阵中放入m种颜色(标号为1 ...

  4. [USACO2005][POJ2226]Muddy Fields(二分图最小点覆盖)

    题目:http://poj.org/problem?id=2226 题意:给你一个字符矩阵,每个位置只能有"*"或者“.",连续的横着或者竖的“*"可以用一块木 ...

  5. POJ2226 Muddy Fields(二分图最小点覆盖集)

    题目给张R×C的地图,地图上*表示泥地..表示草地,问最少要几块宽1长任意木板才能盖住所有泥地,木板可以重合但不能盖住草地. 把所有行和列连续的泥地(可以放一块木板铺满的)看作点且行和列连续泥地分别作 ...

  6. nyoj 237 游戏高手的烦恼 二分匹配--最小点覆盖

    题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=237 二分匹配--最小点覆盖模板题 Tips:用邻接矩阵超时,用数组模拟邻接表WA,暂时只 ...

  7. poj2226 Muddy Fields 填充棒子(二分匹配)

    参考博客:https://blog.csdn.net/liujc_/article/details/51287019 参考博客:https://blog.csdn.net/acdreamers/art ...

  8. UVA 11419 SAM I AM(最大二分匹配&最小点覆盖:König定理)

    题意:在方格图上打小怪,每次可以清除一整行或一整列的小怪,问最少的步数是多少,又应该在哪些位置操作(对输出顺序没有要求). 分析:最小覆盖问题 这是一种在方格图上建立的模型:令S集表示“行”,T集表示 ...

  9. 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 ...

随机推荐

  1. 小程序支持npm包

  2. Oracle、SQLServer 删除表中的重复数据,只保留一条记录

    原文地址: https://blog.csdn.net/yangwenxue_admin/article/details/51742426 https://www.cnblogs.com/spring ...

  3. Async Task Types in C#

    Async Task Types in C# Extend async to support task types that match a specific pattern, in addition ...

  4. yum 时一直停在Determining fastest mirrors 界面

    [root@fanyk ~]# yum redis Loaded plugins: fastestmirror Determining fastest mirrors 在yum makecache时, ...

  5. IDEA内存设置

    之前,博主的idea一直运行正常,今天,开发了一个接口,因为接口在项目中另一个模块,之前从来没有启动过这个模块,由于调试接口将该模块也放到tomcat容器中,结果我的idea不停崩溃,于是查看idea ...

  6. [转]html中meta作用

    meta是html语言head区的一个辅助性标签.几乎所有的网页里,我们可以看到类似下面这段的html代码:  <head>  <meta http-equiv="cont ...

  7. LC 962. Maximum Width Ramp

    Given an array A of integers, a ramp is a tuple (i, j) for which i < j and A[i] <= A[j].  The ...

  8. props的写法

    简写 props: ['demo-first', 'demo-second'] 带类型 props: { 'demo-first': Number, 'demo-second': Number } 带 ...

  9. [Python]python-jenkins 启动需要参数的job

    需求: 我要用python通过api,启动这个job,并且启动这个job需要1个参数 安装依赖: pipenv install python-jenkins 熟悉API的使用方法: 了解一个API的最 ...

  10. flutter vscode 小插件

    dart flutter Awesome Flutter Snippets Bracket Pair Colorizer