4261: 建设游乐场
Time Limit: 50 Sec Memory Limit: 256 MBSubmit: 38 Solved: 16
[Submit][Status][Discuss]
Description
![](http://www.lydsy.com/JudgeOnline/upload/201509/fb.jpg)
Input
Output
Sample Input
1 1 1
1 0 0
1 0 0
48 94 1
78 78 81
1 12 60
Sample Output
HINT
N<=150,M<=30,Vi,j<=100
Source
分析:
好题~~~
考虑构成若干个环的必要条件:每个格子的管道都要链接相邻的两个格子...然后把一个格子拆成两个管子,如果两个管子方向不同,则可以得到价值,如果相同就无法得到...
既然是网格图,那么我们考虑黑白染色,对于每个必须放的格子,我们把这个格子拆成三个点,一个点叫做控制点,一个代表竖向管道,一个代表横向管道...
然后对于每个黑点,从$S$向黑点的控制点连边$<S,id[i][j][0],2,0>$,代表连接两个格子,$<id[i][j][0],id[i][j][1],1,val>$,$<id[i][j][0],id[i][j][1],id[i][j][2],1,val>$,$<id[i][j][0],id[i][j][1],1,0>$,$<id[i][j][0],id[i][j][2],1,0>$...
对于每个白点,从白点的控制点向$T$连边$<id[i][j][0],T,2,0>$,然后$1$、$2$的连边和黑点相反...
然后对于每个黑点,我们把竖向点向上下的相邻格子个竖点连边$<1,0>$,横向点向左右的横向点连边$<1,0>$...
这样,如果满流并且黑白点相同的话就一定是合法解,因为代表每个格子都连接了相邻的两个格子...然后如果一个各自选择了相同方向的管道,那么会得到$val$的收益,如果选择不同的,会得到$2*val$的收益,所以求出最大费用之后减去$\sum val$就好了...
代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
//by NeighThorn
#define inf 0x3f3f3f3f
#define min(a,b) a>b?b:a
using namespace std;
const int N=150+5,maxn=50000+5,maxm=1000000+5;
int n,m,S,T,cnt,tat,tot,sum,mp[N][N],id[N][N][3],val[N][N];
int w[maxm],fl[maxm],hd[maxn],to[maxm],nxt[maxm],Min[maxn],dis[maxn],vis[maxn],from[maxn];
int mv[4][2]={1,0,-1,0,0,1,0,-1};
inline int read(void){
char ch=getchar();int x=0;
while(!(ch>='0'&&ch<='9')) ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
inline void add(int x,int y,int l,int s){
w[cnt]= s;fl[cnt]=l;to[cnt]=y;nxt[cnt]=hd[x];hd[x]=cnt++;
w[cnt]=-s;fl[cnt]=0;to[cnt]=x;nxt[cnt]=hd[y];hd[y]=cnt++;
}
inline bool spfa(void){
memset(Min,inf,sizeof(Min));
memset(dis,inf,sizeof(dis));
queue<int> q;q.push(S);dis[S]=0;vis[S]=1;
while(!q.empty()){
int top=q.front();q.pop();vis[top]=0;
for(int i=hd[top];i!=-1;i=nxt[i])
if(dis[to[i]]>dis[top]+w[i]&&fl[i]){
from[to[i]]=i;
dis[to[i]]=dis[top]+w[i];
Min[to[i]]=min(Min[top],fl[i]);
if(!vis[to[i]])
vis[to[i]]=1,q.push(to[i]);
}
}
return dis[T]!=inf;
}
inline int find(void){
for(int i=T;i!=S;i=to[from[i]^1])
fl[from[i]]-=Min[T],fl[from[i]^1]+=Min[T];
return Min[T]*dis[T];
}
inline int mcmf(void){
int res=0,flow=0;
while(spfa())
res+=find(),flow+=Min[T];
if(flow!=tot) return -1;
return -res-sum;
}
signed main(void){
n=read();m=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
mp[i][j]=read();
for(int i=0;i<=n+1;i++) mp[i][0]=mp[i][m+1]=1;
for(int i=0;i<=m+1;i++) mp[0][i]=mp[n+1][i]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
val[i][j]=read();
if(!mp[i][j])
sum+=val[i][j];
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(!mp[i][j])
for(int k=0;k<3;k++)
id[i][j][k]=++tot;
S=0,T=++tot;tot=0;
memset(hd,-1,sizeof(hd));
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(!mp[i][j]){
if((i+j)&1)
tat+=2,
add(S,id[i][j][0],2,0),
add(id[i][j][0],id[i][j][1],1,0),
add(id[i][j][0],id[i][j][2],1,0),
add(id[i][j][0],id[i][j][1],1,-val[i][j]),
add(id[i][j][0],id[i][j][2],1,-val[i][j]);
else
tot+=2,
add(id[i][j][0],T,2,0),
add(id[i][j][1],id[i][j][0],1,0),
add(id[i][j][2],id[i][j][0],1,0),
add(id[i][j][1],id[i][j][0],1,-val[i][j]),
add(id[i][j][2],id[i][j][0],1,-val[i][j]);
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(!mp[i][j]&&((i+j)&1)){
for(int k=0,x,y;k<4;k++){
x=i+mv[k][0],y=j+mv[k][1];
if(!mp[x][y]){
if(k<2)
add(id[i][j][1],id[x][y][1],1,0);
else
add(id[i][j][2],id[x][y][2],1,0);
}
}
}
if(tat!=tot) return puts("-1"),0;
printf("%d\n",mcmf());
return 0;
}
By NeighThorn