LuoguP2774 方格取数问题(最小割)
题目背景
none!
题目描述
在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意 2 个数所在方格没有公共边,且取出的数的总和最大。试设计一个满足要求的取数算法。对于给定的方格棋盘,按照取数要求编程找出总和最大的数。
输入输出格式
输入格式:
第 1 行有 2 个正整数 m 和 n,分别表示棋盘的行数和列数。接下来的 m 行,每行有 n 个正整数,表示棋盘方格中的数。
输出格式:
程序运行结束时,将取数的最大总和输出
解题思路:
想个办法将选一个点和不选周围点关联起来。
那就是将其连到一条线上,这样变成了,想要割开源汇点,不割掉这个点,就要割掉相连的点。
这就变成了最小割,将棋盘和白染色,使与黑格相连的格都不是黑色。
源点与黑点连,黑点与临近点连,白点与汇点连。
跑最小割就好了。
代码:
1 #include<queue> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 const int oo=0x3f3f3f3f; 6 struct pnt{ 7 int hd; 8 int lyr; 9 int now; 10 }p[10001]; 11 struct ent{ 12 int twd; 13 int lst; 14 int vls; 15 }e[1000000]; 16 int cnt; 17 int n,m; 18 int s,t; 19 int di[4]={0,0,1,-1}; 20 int dj[4]={1,-1,0,0}; 21 int no[101][101]; 22 int vo[101][101]; 23 std::queue<int>Q; 24 void ade(int f,int t,int v) 25 { 26 cnt++; 27 e[cnt].twd=t; 28 e[cnt].vls=v; 29 e[cnt].lst=p[f].hd; 30 p[f].hd=cnt; 31 return ; 32 } 33 bool Bfs(void) 34 { 35 while(!Q.empty()) 36 Q.pop(); 37 for(int i=1;i<=t;i++) 38 p[i].lyr=0; 39 p[s].lyr=1; 40 Q.push(s); 41 while(!Q.empty()) 42 { 43 int x=Q.front(); 44 Q.pop(); 45 for(int i=p[x].hd;i;i=e[i].lst) 46 { 47 int to=e[i].twd; 48 if(p[to].lyr==0&&e[i].vls>0) 49 { 50 p[to].lyr=p[x].lyr+1; 51 if(to==t) 52 return true; 53 Q.push(to); 54 } 55 } 56 } 57 return false; 58 } 59 int Dfs(int x,int fll) 60 { 61 if(x==t) 62 return fll; 63 for(int& i=p[x].now;i;i=e[i].lst) 64 { 65 int to=e[i].twd; 66 if(p[to].lyr==p[x].lyr+1&&e[i].vls>0) 67 { 68 int ans=Dfs(to,std::min(fll,e[i].vls)); 69 if(ans>0) 70 { 71 e[i].vls-=ans; 72 e[((i-1)^1)+1].vls+=ans; 73 return ans; 74 } 75 } 76 } 77 return 0; 78 } 79 int Dinic(void) 80 { 81 int ans=0; 82 while(Bfs()) 83 { 84 for(int i=1;i<=t;i++) 85 p[i].now=p[i].hd; 86 int dlt; 87 while(dlt=Dfs(s,oo)) 88 ans+=dlt; 89 } 90 return ans; 91 } 92 int main() 93 { 94 // freopen("a.in","r",stdin); 95 int sum=0; 96 scanf("%d%d",&n,&m); 97 for(int i=1;i<=n;i++) 98 { 99 for(int j=1;j<=m;j++) 100 { 101 scanf("%d",&vo[i][j]); 102 no[i][j]=++cnt; 103 sum+=vo[i][j]; 104 } 105 } 106 s=n*m+1; 107 t=s+1; 108 cnt=0; 109 for(int i=1;i<=n;i++) 110 { 111 for(int j=1;j<=m;j++) 112 { 113 if((i+j)%2==0) 114 { 115 ade(s,no[i][j],vo[i][j]); 116 ade(no[i][j],s,0); 117 for(int d=0;d<4;d++) 118 { 119 int ii=i+di[d],jj=j+dj[d]; 120 if(ii<=0||jj<=0||ii>n||jj>m) 121 continue; 122 ade(no[i][j],no[ii][jj],oo); 123 ade(no[ii][jj],no[i][j],0); 124 } 125 }else{ 126 ade(no[i][j],t,vo[i][j]); 127 ade(t,no[i][j],0); 128 } 129 } 130 } 131 printf("%d\n",sum-Dinic()); 132 return 0; 133 }