一道很不错的网络流题目...
刚看到这题感觉无从下手,关键是要发现一个性质:对于每一个合法的方案,每一个平原块都只和与他相邻的两个平原块相连,这样我们就能够通过最大流的模型来判断是否有解了。
那么对于带权的情况,我们考虑当前点的权值是否可以取到只取决于与当前点连接的两个平原块是否是一个方向,这样我们对一个平原块拆成2个点,分别处理X方向和Y方向,为了限制这两个方向流量和为2,我们还要再加一个点来限流,这样,如果这个2的流量是分别从X方向和Y方向流出,那么当前点的权值就能够得到,否则就不行,这个限制转化一下,变成如果当前这2个流量从一个点流出,就要花费权值的代价,那么我们可以利用费用流,通过拆边的方法进行处理,这样这个问题就解决了。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 #define M 200000 6 #define N 20000 7 #define NN 305 8 int last[N],pre[M],other[M],cap[M],cost[M]; 9 int id[NN][NN],idx[NN][NN],idy[NN][NN]; 10 int n,m,tot,l,S,T,ans,flow_sum,que[M+1],dis[N],next[N]; 11 bool vis[N]; 12 13 inline int read(void) 14 { 15 int x=0; 16 char ch=getchar(); 17 while (ch>'9'||ch<'0') ch=getchar(); 18 while (ch>='0'&&ch<='9') 19 { 20 x=x*10+ch-'0'; 21 ch=getchar(); 22 } 23 return x; 24 } 25 26 void connect(int x,int y,int z,int w) 27 { 28 l++; 29 pre[l]=last[x]; 30 last[x]=l; 31 other[l]=y; 32 cap[l]=z; 33 cost[l]=w; 34 swap(x,y); 35 l++; 36 pre[l]=last[x]; 37 last[x]=l; 38 other[l]=y; 39 cap[l]=0; 40 cost[l]=-w; 41 } 42 43 bool spfa(void) 44 { 45 memset(dis,53,sizeof dis); 46 memset(next,0,sizeof next); 47 dis[S]=0;que[1]=S; 48 int h=0,t=1; 49 while (h!=t) 50 { 51 h=h%M+1; 52 int u=que[h];vis[u]=0; 53 for (int p=last[u];p;p=pre[p]) 54 { 55 if (cap[p]==0) continue; 56 int v=other[p]; 57 if (dis[v]>dis[u]+cost[p]) 58 { 59 dis[v]=dis[u]+cost[p]; 60 next[v]=p; 61 if (vis[v]) continue; 62 t=t%M+1; 63 que[t]=v; 64 } 65 } 66 } 67 return dis[T]<dis[0]; 68 } 69 70 void MCMF(void) 71 { 72 while (spfa()) 73 { 74 int flow=1e9; 75 for (int p=next[T];p;p=next[other[p^1]]) flow=min(flow,cap[p]); 76 for (int p=next[T];p;p=next[other[p^1]]) 77 cap[p]-=flow,cap[p^1]+=flow; 78 ans-=flow*dis[T]; 79 flow_sum-=flow; 80 } 81 } 82 83 int main() 84 { 85 n=read();m=read();l=1; 86 S=++tot;T=++tot; 87 for (int i=1;i<=n;i++) 88 for (int j=1;j<=m;j++) 89 { 90 int x=read(); 91 if (x==1) continue; 92 flow_sum++; 93 id[i][j]=++tot; 94 idx[i][j]=++tot; 95 idy[i][j]=++tot; 96 } 97 for (int i=1;i<=n;i++) 98 for (int j=1;j<=m;j++) 99 { 100 int x=read(); 101 if (!id[i][j]) continue; 102 ans+=x; 103 if ((i+j)&1) 104 { 105 connect(S,id[i][j],2,0); 106 connect(id[i][j],idx[i][j],1,0); 107 connect(id[i][j],idy[i][j],1,0); 108 connect(id[i][j],idx[i][j],1,x); 109 connect(id[i][j],idy[i][j],1,x); 110 if (i+1<=n&&id[i+1][j]) connect(idx[i][j],idx[i+1][j],1,0); 111 if (i-1>=1&&id[i-1][j]) connect(idx[i][j],idx[i-1][j],1,0); 112 if (j+1<=m&&id[i][j+1]) connect(idy[i][j],idy[i][j+1],1,0); 113 if (j-1>=1&&id[i][j-1]) connect(idy[i][j],idy[i][j-1],1,0); 114 } 115 else 116 { 117 connect(id[i][j],T,2,0); 118 connect(idx[i][j],id[i][j],1,0); 119 connect(idy[i][j],id[i][j],1,0); 120 connect(idx[i][j],id[i][j],1,x); 121 connect(idy[i][j],id[i][j],1,x); 122 } 123 } 124 MCMF(); 125 flow_sum?puts("-1"):printf("%d\n",ans); 126 return 0; 127 }