Libre 6007 「网络流 24 题」方格取数 / Luogu 2774 方格取数问题 (网络流,最大流)
2017-08-04 19:55
423 查看
Libre 6007 「网络流 24 题」方格取数 / Luogu 2774 方格取数问题 (网络流,最大流)
Description
在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意 2 个数所在方格没有公共边,且取出的数的总和最大。试设计一个满足要求的取数算法。对于给定的方格棋盘,按照取数要求编程找出总和最大的数。Input
第 1 行有 2 个正整数 m 和 n,分别表示棋盘的行数和列数。接下来的 m 行,每行有 n 个正整数,表示棋盘方格中的数。Output
程序运行结束时,将取数的最大总和输出Sample Input
3 31 2 3
3 2 3
2 3 1
Sample Output
11Http
Libre:https://loj.ac/problem/6007Luogu:https://www.luogu.org/problem/show?pid=2774
Source
网络流,最大流解决思路
总觉的这道题能想到用网络流解很玄学网络流的另外一种模型:点覆盖模型,就是有诸如两个只能选一个的限制这类,没有做过的话根本想不到。
我们把网格黑白染色,让黑格周围都是白格,白格同理,简单点来说对于格子(i,j),若i%2==j%2则黑色,否则白色。
另外建立一个源点一个汇点。对于每一个黑色的格子,从源点连一条容量为格子上的数的边,并且向周围的所有相邻白格子连一条容量为无穷大的边。对于每一个白格子,从白格子连一条容量位格子上的数的边到汇点。
同时,统计所有格子上的数之和 记为sum。求出最大流flow,则答案就是flow-sum。
这种解法的原理是基于最小割最大流,由于笔者知识水平不高,暂且不知道如何证明。但可以通过画图手动模拟的方式基本上可以认识到其正确性,即通过巧妙的方式割去其中部分边点,使得剩下的最大(笔者也只能理解到这里了)
另:这里使用Dinic实现最大流,具体可以移步我的这篇文章
代码
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; #define pos(x,y) (x-1)*n+y const int maxN=40; const int inf=2147483647; class Edge { public: int u,v,flow; }; int n,m; int cnt=-1; int Mat[maxN][maxN]; int Head[maxN*maxN]; int Next[maxN*maxN*maxN*maxN]; Edge E[maxN*maxN*maxN*maxN]; int depth[maxN*maxN]; int cur[maxN*maxN]; int Q[maxN*maxN*maxN]; void Add_Edge(int u,int v,int flow); bool bfs(); int dfs(int u,int flow); int main() { memset(Head,-1,sizeof(Head)); int sum=0; scanf("%d%d",&m,&n); for (int i=1;i<=m;i++) for (int j=1;j<=n;j++) { int num; scanf("%d",&num); sum+=num;//sum统计所有格子的权之和 if (i%2==j%2)//若为黑格子 { Add_Edge(0,pos(i,j),num);//连接源点 if (i!=1)//分别连接四周的白格子,注意判断存在与否 Add_Edge(pos(i,j),pos(i-1,j),inf); if (i!=m) Add_Edge(pos(i,j),pos(i+1,j),inf); if (j!=1) Add_Edge(pos(i,j),pos(i,j-1),inf); if (j!=n) Add_Edge(pos(i,j),pos(i,j+1),inf); } else Add_Edge(pos(i,j),n*m+1,num);//若为白格子,则只连接汇点 } int Ans=0; while (bfs())//求解最大流 { memcpy(cur,Head,sizeof(cur)); while (int di=dfs(0,inf)) Ans+=di; } cout<<sum-Ans<<endl; return 0; } void Add_Edge(int u,int v,int flow) { cnt++; Next[cnt]=Head[u]; Head[u]=cnt; E[cnt].u=u; E[cnt].v=v; E[cnt].flow=flow; cnt++; Next[cnt]=Head[v]; Head[v]=cnt; E[cnt].u=v; E[cnt].v=u; E[cnt].flow=0; } bool bfs() { memset(depth,-1,sizeof(depth)); int h=1,t=0; Q[1]=0; depth[0]=1; do { t++; int u=Q[t]; for (int i=Head[u];i!=-1;i=Next[i]) { int v=E[i].v; if ((E[i].flow>0)&&(depth[v]==-1)) { h++; Q[h]=v; depth[v]=depth[u]+1; } } } while (t!=h); if (depth[n*m+1]==-1) return 0; return 1; } int dfs(int u,int flow) { if (u==n*m+1) return flow; for (int i=Head[u];i!=-1;i=Next[i]) { int v=E[i].v; if ((E[i].flow>0)&&(depth[v]==depth[u]+1)) { int di=dfs(v,min(flow,E[i].flow)); if (di>0) { E[i].flow-=di; E[i^1].flow+=di; return di; } } } return 0; }
相关文章推荐
- Libre 6006 「网络流 24 题」试题库 / Luogu 2763 试题库问题 (网络流,最大流)
- Libre 6005 「网络流 24 题」最长递增子序列 / Luogu 2766 最长递增子序列问题(网络流,最大流)
- [网络流24题] 方格取数问题 (最大点权独立集)
- Libre 6009 「网络流 24 题」软件补丁 / Luogu 2761 软件安装问题 (最短路径,位运算)
- [网络流24题] 方格取数问题 (最大权独立集---网络最小割)
- [网络流24题] 09 方格取数问题 (二分图点权最大独立集,最小割)
- 【loj】#6007. 「网络流 24 题」方格取数(二分图最大点权独立集)
- 【网络流24题】方格取数问题(最大流)
- Cogs 734. [网络流24题] 方格取数问题(最大闭合子图)
- 洛谷2774:[网络流24题]方格取数问题——题解
- 【网络流24题】No.9 方格取数问题 (二分图点权最大独立集)
- [网络流24题][CODEVS1922]骑士共存问题(最大流)
- 线性规划与网络流24题 09方格取数问题
- 【网络流24题】 No.3 最小路径覆盖问题 (网络流|匈牙利算法 ->最大二分匹配)
- 【线性规划与网络流24题 9】方格取数问题
- 网络流24题(09)方格取数问题(最大点权独立集 + 最小割最大流)
- Libre 6004 「网络流 24 题」圆桌聚餐(网络流,最大流)
- [网络流24题] 方格取数问题(cogs 734)
- 【网络流24题】方格取数问题
- 【网络流24题】 No.2 太空飞行计划问题 (最大闭合权图 最大流 )