题意:
n,m<=30
思路:
【问题分析】
二分图点权最大独立集,转化为最小割模型,从而用最大流解决。
【建模方法】
首先把棋盘黑白染色,使相邻格子颜色不同,所有黑色格子看做二分图X集合中顶点,白色格子看做Y集合顶点,建立附加源S汇T。
1、从S向X集合中每个顶点连接一条容量为格子中数值的有向边。
2、从Y集合中每个顶点向T连接一条容量为格子中数值的有向边。
3、相邻黑白格子Xi,Yj之间从Xi向Yj连接一条容量为无穷大的有向边。
求出网络最大流,要求的结果就是所有格子中数值之和减去最大流量。
【建模分析】
这是一个二分图最大点权独立集问题,就是找出图中一些点,使得这些点之间没有边相连,这些点的权值之和最大。独立集与覆盖集是互补的,求最大点权独立集可以转化为求最小点权覆盖集(最小点权
支配集)。最小点权覆盖集问题可以转化为最小割问题解决。结论:最大点权独立集 = 所有点权 - 最小点权覆盖集 = 所有点权 - 最小割集 = 所有点权 - 网络最大流。
对于一个网络,除去冗余点(不存在一条ST路径经过的点),每个顶点都在一个从S到T的路径上。割的性质就是不存在从S到T的路径,简单割可以认为割边关联的非ST节点为割点,而在二分图网络流模
型中每个点必关联到一个割点(否则一定还有增广路,当前割不成立),所以一个割集对应了一个覆盖集(支配集)。最小点权覆盖集就是最小简单割,求最小简单割的建模方法就是把XY集合之间的变容
量设为无穷大,此时的最小割就是最小简单割了。
有关二分图最大点权独立集问题,更多讨论见《最小割模型在信息学竞赛中的应用》作者胡伯涛。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 typedef unsigned int uint; 5 typedef unsigned long long ull; 6 typedef long double ld; 7 typedef pair<int,int> PII; 8 typedef pair<ll,ll> Pll; 9 typedef vector<int> VI; 10 typedef vector<PII> VII; 11 typedef pair<ll,ll>P; 12 #define N 100010 13 #define M 1000000 14 #define INF 1e9 15 #define fi first 16 #define se second 17 #define MP make_pair 18 #define pb push_back 19 #define pi acos(-1) 20 #define mem(a,b) memset(a,b,sizeof(a)) 21 #define rep(i,a,b) for(int i=(int)a;i<=(int)b;i++) 22 #define per(i,a,b) for(int i=(int)a;i>=(int)b;i--) 23 #define lowbit(x) x&(-x) 24 #define Rand (rand()*(1<<16)+rand()) 25 #define id(x) ((x)<=B?(x):m-n/(x)+1) 26 #define ls p<<1 27 #define rs p<<1|1 28 29 const ll MOD=1e9+7,inv2=(MOD+1)/2; 30 double eps=1e-6; 31 int dx[4]={-1,1,0,0}; 32 int dy[4]={0,0,-1,1}; 33 34 int head[N],vet[N],len[N],nxt[N],dis[N], 35 a[50][50],b[50][50],num[50][50],s,S,T,tot; 36 37 int read() 38 { 39 int v=0,f=1; 40 char c=getchar(); 41 while(c<48||57<c) {if(c=='-') f=-1; c=getchar();} 42 while(48<=c&&c<=57) v=(v<<3)+v+v+c-48,c=getchar(); 43 return v*f; 44 } 45 46 void add(int a,int b,int c) 47 { 48 nxt[++tot]=head[a]; 49 vet[tot]=b; 50 len[tot]=c; 51 head[a]=tot; 52 53 nxt[++tot]=head[b]; 54 vet[tot]=a; 55 len[tot]=0; 56 head[b]=tot; 57 } 58 59 bool bfs() 60 { 61 queue<int>q; 62 rep(i,1,s) dis[i]=-1; 63 q.push(S),dis[S]=0; 64 while(!q.empty()) 65 { 66 int u=q.front(); 67 q.pop(); 68 int e=head[u]; 69 while(e) 70 { 71 int v=vet[e]; 72 if(len[e]&&dis[v]==-1) 73 { 74 dis[v]=dis[u]+1; 75 q.push(v); 76 } 77 e=nxt[e]; 78 } 79 } 80 return dis[T]!=-1; 81 } 82 83 int dfs(int u,int aug) 84 { 85 if(u==T) return aug; 86 int e=head[u],val=0,flow=0; 87 while(e) 88 { 89 int v=vet[e]; 90 if(len[e]&&dis[v]==dis[u]+1) 91 { 92 int t=dfs(v,min(len[e],aug)); 93 if(!t) 94 { 95 e=nxt[e]; 96 continue; 97 } 98 flow+=t; 99 aug-=t; 100 len[e]-=t; 101 len[e^1]+=t; 102 if(!aug) break; 103 } 104 e=nxt[e]; 105 } 106 if(!flow) dis[u]=-1; 107 return flow; 108 } 109 110 int maxflow() 111 { 112 int res=0; 113 while(bfs()) res+=dfs(S,INF); 114 return res; 115 } 116 117 int main() 118 { 119 int n=read(),m=read(); 120 int ans=0; 121 rep(i,1,n) 122 rep(j,1,m) 123 { 124 a[i][j]=read(); 125 ans+=a[i][j]; 126 } 127 128 tot=1; 129 s=0; 130 rep(i,1,n) 131 rep(j,1,m) num[i][j]=++s; 132 133 rep(i,1,n) 134 rep(j,1,m) 135 if((i+j+1)&1) 136 rep(k,0,3) 137 { 138 int x=i+dx[k],y=j+dy[k]; 139 if(x>0&&x<=n&&y>0&&y<=m) add(num[i][j],num[x][y],INF); 140 } 141 S=++s,T=++s; 142 rep(i,1,n) 143 rep(j,1,m) 144 if((i+j+1)&1) add(S,num[i][j],a[i][j]); 145 else add(num[i][j],T,a[i][j]); 146 ans-=maxflow(); 147 printf("%d ",ans); 148 return 0; 149 }