网络流24题--方格取数问题
来源:互联网 发布:centos 6.5 ftp客户端 编辑:程序博客网 时间:2024/05/04 17:54
若有疏漏,敬请指出不足之处,谢谢!!!
最近刷了网络流的不少题目,,,所以决定总结一下关于方格取数问题的基本做法。
模版题 Hdu 1565
题意
大概的题意是:
给你一个n*n的格子的棋盘,每个格子里面有一个非负数。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大。
Hdu 1565就是这样的题目。
通常做法
对于这类题目,甚至可以推广到只要有方格类的。我们可以考虑将其黑白染色,如图,
假设其点权为2,1,3,4,作为一个例子
方格就转化为二分图,相邻的格子连边,求最大点权独立集。
知识原理和定义
最大点权独立集的定义是,在二分图中选定一些点,使这些点不被边直接相连的同时满足点权和最大。
最小点权覆盖是指,在二分图中选定一些点,使这些点能够覆盖所有边,即满足所有边的其中一个点是选定点的一个。
众所周知(在这里就不证明了),最大点权独立集等于点权和减去最小点权覆盖,你们可以试验一下。
也就是在二分图中选定若干点后满足最小点权覆盖,没选定的点即构成最大点权独立集。
对于解决最小点权覆盖,采用最小割方式解决,网络流通常是把限制条件转化成边权来跑。
建图
所以构造源点,连接源点和被染成黑色的点,边权为该点的点权,相邻的点边权为inf
如图:
建图思路
然后求最小割,就是最小点权覆盖
证明如下:
我们知道如果存在最小割,那么任何形如S-u-v-T的线路上必定是不通的(即有割从中经过,否则不满足割的性质)
因为是求最小割,而u-v的边权是inf,因此割不会从此经过,也就是从
S-u,v-T中选一个经过。
选了一条边之后,边上除了源点和汇点的另外一个点即视为选中,
所以当有割时,任意一个S-u-v-T的线路肯定是u或者v被选中,
也就是满足所有边都覆盖。
当是最小割时,点权覆盖也为最小。
所以最小割=最小点权覆盖,
同时又最大流最小割定理可知,最大流=最小割,所以这类题目可以跑一下网络流最大流来完成。
AC代码
Hdu 1565代码如下:
#include <iostream>#include <cstdio>#include <queue>#include <cstring>#include <cstdlib>using namespace std;#define MAXN 21#define inf 0x7fffffffstruct Edge{ int v,w,nxt;}g[13*MAXN*MAXN];int head[MAXN*MAXN+1];int work[MAXN*MAXN+1];int cnt;void addEdge(int u,int v,int w){ g[cnt].v = v; g[cnt].w = w; g[cnt].nxt = head[u]; head[u] = cnt; ++ cnt;}int n;int a[MAXN+1][MAXN+1];bool cur[MAXN+1][MAXN+1];bool f = true,flag = true;int S,T;int sum,ans,flow;int dis[MAXN*MAXN+1];queue<int> q;int cal(int x,int y){ return (x-1)*n+y; }void init(){ cnt = ans = flow = sum = 0; memset(g,0,sizeof(g)); memset(head,-1,sizeof(head)); memset(cur,0,sizeof(cur)); f = true; flag = true;}bool bfs(){ memset(dis,-1,sizeof(dis)); while(!q.empty()) q.pop(); q.push(S);dis[S]=0; while(!q.empty()){ int u = q.front();q.pop(); for(int i=head[u];i!=-1;i=g[i].nxt){ int v = g[i].v; if((dis[v]==-1)&& g[i].w>0){ dis[v] = dis[u]+1; q.push(v); } } } return (dis[T]!=-1);}int dfs(int u,int exp){ if(u==T) return exp; int tmp = 0; for(int &i=work[u];i!=-1;i=g[i].nxt){ int v = g[i].v; if((dis[v]==dis[u]+1)&&(g[i].w>0)){ tmp = dfs(v,min(exp,g[i].w)); if(!tmp) continue; g[i].w -= tmp; g[i^1].w += tmp; return tmp; } } return 0;}int main(){ while(~scanf("%d",&n)){ S = 0; T = n*n+1; init(); for(int i=1;i<=n;++i){ f=flag; flag=!flag; for(int j=1;j<=n;++j){ scanf("%d",&a[i][j]); sum += a[i][j]; cur[i][j] = f; f = !f; } } for(int i=1;i<=n;++i){ for(int j=1;j<=n;++j){ int tmp = cal(i,j); if(cur[i][j]){ addEdge(S,tmp,a[i][j]); addEdge(tmp,S,0); if(i>1){ addEdge(tmp,cal(i-1,j),inf); addEdge(cal(i-1,j),tmp,0); } if(i<n){ addEdge(tmp,cal(i+1,j),inf); addEdge(cal(i+1,j),tmp,0); } if(j>1){ addEdge(tmp,cal(i,j-1),inf); addEdge(cal(i,j-1),tmp,0); } if(j<n){ addEdge(tmp,cal(i,j+1),inf); addEdge(cal(i,j+1),tmp,0); } } else{ addEdge(tmp,T,a[i][j]); addEdge(T,tmp,0); } } } while(bfs()){ memcpy(work,head,sizeof(head)); while(flow=dfs(S,inf)){ ans += flow; } } printf("%d\n",sum-ans); }}
进阶题Hdu 1569
还有Hdu 1569也是几乎完全一模一样的,只不过由n*n的方格改为
n*m罢了,没有什么区别,注意下细节就行了。
Hdu 3820则是一道值得一做的拓展题了。
题意
题意是给一个方格阵,每个方格可以放金蛋或者银蛋,放不同的蛋会分别拿到各自的分数,
但是,如果相邻的方格颜色一样,如果都是金色,就需要减掉G,否则减掉S,
现在求最大分数。
建图
其实这道题是方格取数的加强版,道理是一样的。
将所有方格黑白染色,
因为一个方格有金银两个状态,所以可以通过拆点的方法来做,
为了便于说明,定义黑 为黑色方格放金蛋的点,黑’为放银蛋,白为白色方格放金蛋,白’为银色
tips:拆点是网络流最基本的方法,必须要掌握。
为构造图使满足相同颜色的约束条件,所以黑 必须连接 白,而黑安排在左边,所以白安排在右边
所以源点连接黑,边权为该方格分数,也连接白’
黑‘->T,白->T
但为什么不能够源点连黑、黑’,白、白‘连汇点呢?解释如下
建图思路
首先肯定要黑连接黑’,权值为inf,使得黑和黑’只能选择一个,道理和前面的方格取数一样,这样的话即指黑和黑’之间存在边。
注意到一个细节,我们要转化成最大点权独立集,而是存在于二分图中的,如果黑、黑‘在二分图同一边,它们存在边,与二分图定义矛盾。
然后白’也连接白
此时黑连接白,权值为G,
因为我们知道求最大分数是求最大点权独立集=总点权-最小点权覆盖,
所以最小点权覆盖选择的边,就是最大点权独立集不选的边,
而最小点权覆盖=最小割,
所以最小割选择的边中不包括S、T的点就等于最小点权覆盖所需的点。
以一个图来做例子(题目中的第一个样例):
建图后如图所示:
经计算可以知道,最小割为以下红色的边:
那么也就是只需要选择1、4、3、1‘、4’、2就能实现完全覆盖,
所以选2‘、3构成最大点权独立集。
所以所以所有点中不包括最小割经过的边中不包括S、T的点的点,是最大点权独立集的点
因为最大点权独立集=点权和-最小割,
而相邻方格取同样颜色是要减去费用的,也就是可以体现在最小割上,所以可以通过构造边来实现,
最后,可以再次考虑一个S-u-v-T的路径,其中u,v代表相同颜色(金银)且相邻的方格。
那么这条路径由于割的性质不能够联通,
因此最小割只能经过S-u,v-T的其中一条边,或者经过u-v的边
这就意味着:
两个相邻的方格,如果要选同样颜色(金银),要不在两个当中只选一个,否则两个都选的话,就得多花费用(u-v边权)
这样就能够满足题意了,题目也就能够解决了。
代码
代码如下:
#include <iostream>#include <cstdio>#include <cstring>#include <queue>using namespace std;#define MAXN 50001#define inf 0x7fffffffstruct Edge{ int v,w,nxt;}g[MAXN];int head[MAXN];int cur[MAXN];int cnt;void addEdge(int u,int v,int w){ g[cnt] = (Edge){v,w,head[u]}; head[u] = cnt; ++ cnt;}int Tc;int n,m,x,y;int S,T;queue<int> q;int ans,sum;int dis[MAXN];int a[101][101],b[101][101];int dx[5]={0,1,0,-1,0};int dy[5]={0,0,1,0,-1};void init(){ sum = ans = cnt = 0; memset(g,0,sizeof(g)); memset(head,-1,sizeof(head));}inline int C(int i,int j){ return ((i-1)*m+j);}bool bfs(){ memset(dis,-1,sizeof(dis)); while(!q.empty()) q.pop(); q.push(S);dis[S]=0; while(!q.empty()){ int u = q.front();q.pop(); for(int i=head[u];i!=-1;i=g[i].nxt){ int v = g[i].v; if((dis[v]==-1)&&(g[i].w>0)){ dis[v] = dis[u]+1; if(v==T) return true; q.push(v); } } } return (dis[T]!=-1);}int dfs(int u,int exp){ if(u==T) return exp; int tmp = 0,left = 0; for(int &i=cur[u];i!=-1;i=g[i].nxt){ int v = g[i].v; if((dis[v]==dis[u]+1)&&(g[i].w>0)){ tmp = dfs(v,min(exp,g[i].w)); if(!tmp){ dis[v]=-1;continue; } g[i].w -= tmp; g[i^1].w += tmp; left += tmp; exp -= tmp; if(!exp) break; } } if(exp) dis[u]=-1; return left;}int main(){ scanf("%d",&Tc); for(int Cases=1;Cases<=Tc;++Cases){ init(); scanf("%d%d%d%d",&n,&m,&x,&y); for(int i=1;i<=n;++i) for(int j=1;j<=m;++j){ scanf("%d",&a[i][j]);sum += a[i][j]; } for(int i=1;i<=n;++i) for(int j=1;j<=m;++j){ scanf("%d",&b[i][j]);sum += b[i][j]; } S = 0; T = (n*m)*2 +1; for(int i=1;i<=n;++i){ for(int j=1;j<=m;++j){ if((i+j)%2==0){ addEdge(S,C(i,j),a[i][j]); addEdge(C(i,j),S,0); addEdge(n*m+C(i,j),T,b[i][j]); addEdge(T,n*m+C(i,j),0); addEdge(C(i,j),n*m+C(i,j),inf); addEdge(n*m+C(i,j),C(i,j),0); for(int k=1;k<=4;++k){ int sx = i+dx[k],sy = j+dy[k]; if((sx>0)&&(sx<=n)&&(sy>0)&&(sy<=m)){ addEdge(C(i,j),C(sx,sy),x); addEdge(C(sx,sy),C(i,j),0); } } } else{ addEdge(S,n*m+C(i,j),b[i][j]); addEdge(n*m+C(i,j),S,0); addEdge(C(i,j),T,a[i][j]); addEdge(T,C(i,j),0); addEdge(n*m+C(i,j),C(i,j),inf); addEdge(C(i,j),n*m+C(i,j),0); for(int k=1;k<=4;++k){ int sx = i+dx[k],sy = j+dy[k]; if((sx>0)&&(sx<=n)&&(sy>0)&&(sy<=m)){ addEdge(n*m+C(i,j),n*m+C(sx,sy),y); addEdge(n*m+C(sx,sy),n*m+C(i,j),0); } } } } } while(bfs()){ memcpy(cur,head,sizeof(head)); ans += dfs(S,inf); } printf("Case %d: %d\n",Cases,sum-ans); }}
- [网络流24题 #9]方格取数问题
- 方格取数问题[网络流24题之9]
- 【网络流24题】方格取数问题
- 网络流24题9. 方格取数问题
- 网络流24题之T9 方格取数问题
- cogs 734. [网络流24题] 方格取数问题
- COGS 734. [网络流24题] 方格取数问题
- 网络流24题--方格取数问题
- [网络流 24 题] 方格取数问题 骑士共存问题
- HDU 方格取数(网络流24题,八)
- 【codevs1227】[网络流24题]方格取数 2
- 【codevs1907】[网络流24题]方格取数3
- 「网络流 24 题」方格取数
- 「网络流 24 题」方格取数
- 线性规划与网络流24题 09方格取数问题
- 网络流与线性规划24题09方格取数问题
- 线性规划与网络流24题之方格取数问题 二分图点权最大独立集
- [网络流24题] 09 方格取数问题 (二分图点权最大独立集,最小割)
- PHPExcel导出示例
- 在本地仓库有jar包的情况下,Maven工程依然报错Missing jar包的解决办法
- sge集群配置
- 各种坑
- atoX 与 strtoX
- 网络流24题--方格取数问题
- vscode中配置golang开发环境
- PAT (Advanced Level) Practise 1102 Invert a Binary Tree (25)
- java及Python处理异常的方式和各自的异常种类
- 三.BeautifulSoup用法大全
- 收集别人的博客
- 二.lxml用法大全
- JAVA 动态代理
- ViewPager