[bzoj4657]tower
建图真的学不来。。
首先我们考虑把题目转化成最小割。
然后这个题有些性质就是每个点最多有两种被选中的方案,然后我们想最小割的话就应该设成负数的流量(我们在这里强制加一个INF什么的就行了)。
然后关于只能选一个的限制我们参考切糕。
然后就有了建图,行列分别连S,T然后中间连INF的限制就行了。详见代码
- 代码
#include<bits/stdc++.h>
using namespace std;
const int N=10010,M=2e6+5;
const int INF=0x3f3f3f3f;
int n,m;
namespace graph{
int hed[N],cnt=1,to[M],nxt[M],cap[M],flow[M];
int S,T;
int d[N];bool vis[N];
void adde(int u,int v,int _cap){
++cnt;to[cnt]=v,cap[cnt]=_cap,nxt[cnt]=hed[u],flow[cnt]=0;hed[u]=cnt;
++cnt;to[cnt]=u,cap[cnt]=0,nxt[cnt]=hed[v],flow[cnt]=0;hed[v]=cnt;
}
queue<int>Q;
bool bfs(){
memset(vis,0,sizeof(vis));
memset(d,0,sizeof(d));
Q.push(S);vis[S]=1;
while(!Q.empty()){
int u=Q.front();Q.pop();
for(int i=hed[u];i;i=nxt[i]){
int v=to[i];
if(!vis[v]&&cap[i]>flow[i]){
vis[v]=1,d[v]=d[u]+1;
Q.push(v);
}
}
}
return vis[T];
}
int dfs(int u,int F){
if(u==T||!F)return F;
int flownow=0;
for(int i=hed[u];i;i=nxt[i]){
int v=to[i];if(d[v]!=d[u]+1)continue;
int f=dfs(v,min(F,cap[i]-flow[i]));
flownow+=f;
F-=f;
flow[i]+=f;
flow[i^1]-=f;
if(!F)break;
}
if(!flownow)d[u]=0;
return flownow;
}
int max_flow(){
int fw=0;
while(bfs()){
fw+=dfs(S,INF);
}
return fw;
}
}
int id(int x,int y,int w){
return w*n*m+(x-1)*m+y;
}
int a[100][100];
const int dx[] = {-1, 1, 0, 0}, dy[] = {0, 0, -1, 1};
int ans=0;
int main(){
using namespace graph;
scanf("%d%d",&n,&m);
S=0,T=n*m*2+1;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)scanf("%d",&a[i][j]);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)
if(a[i][j]<0){
int mx=0,x=i,y=j,t=-a[i][j]-1;
while(1){
x+=dx[t],y+=dy[t];
if(x<1||x>n||y<1||y>m)break;
mx=max(mx,a[x][y]);
}
ans+=mx;
if(t<2){
adde(S,id(i,j,0),INF);
}else{
adde(id(i,j,1),T,INF);
}
x=i,y=j;
while(1){
int xx=x,yy=y;
x+=dx[t],y+=dy[t];
if(x<1||x>n||y<1||y>m)break;
if(t<2){
adde(id(xx,yy,0),id(x,y,0),mx-max(0,a[xx][yy]));
}else{
adde(id(x,y,1),id(xx,yy,1),mx-max(0,a[xx][yy]));
}
}
}else{
adde(id(i,j,0),id(i,j,1),INF);
}
}
printf("%d\n",ans-max_flow());
}