P2774 方格取数问题

emm........仔细一看,这不是最大权闭合子图的题吗!

取一个点$(x,y)$,限制条件是同时取$(x,y+1),(x,y-1),(x+1,y),(x-1,y)$,只不过权值取负而已

于是我们把图分为黑点和白点,同颜色点之间不相邻,不同颜色的点相邻(如将$(x+y)%2==1$的点记为黑点)

假装把白点的权值都看成负的

记$link(p,q,val)$为$p$向$q$连一条$val$的边(包括反向边)

蓝后根据最大权闭合子图的套路

对于黑点$p$与相邻的白点$q$

$link(S,p,val_p)$

$link(p,q,inf)$

$link(q,T,val_q)$($val_q$不取负

蓝后就可以愉快地跑最小割辣

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define N 100005
#define inf 1000000000
const int d1[]={,,-,};
const int d2[]={,,,-};
int n,m,d[N],cur[N],tot,S,T; bool vis[N];
queue <int> h;
int cnt=,hd[N],nxt[N],ed[N],poi[N],val[N];
inline void adde(int x,int y,int v){
nxt[ed[x]]=++cnt, hd[x]=hd[x]?hd[x]:cnt,
ed[x]=cnt, poi[cnt]=y, val[cnt]=v;
}
inline void link(int x,int y,int v){adde(x,y,v),adde(y,x,);}
inline int id(int x,int y){return (x-)*m+y;}
bool bfs(){
memset(vis,,sizeof(vis));
h.push(S); vis[S]=;
while(!h.empty()){
int x=h.front(); h.pop();
for(int i=hd[x];i;i=nxt[i]){
int to=poi[i];
if(!vis[to]&&val[i]>)
vis[to]=,d[to]=d[x]+,h.push(to);
}
}return vis[T];
}
int dfs(int x,int a){
if(x==T||a==) return a;
int F=,f;
for(int &i=cur[x];i;i=nxt[i]){
int to=poi[i];
if(d[to]==d[x]+&&(f=dfs(to,min(a,val[i])))>)
a-=f,F+=f,val[i]-=f,val[i^]+=f;
if(!a) break;
}return F;
}
int dinic(){
int re=;
while(bfs()){
for(int i=;i<=T;++i) cur[i]=hd[i];
re+=dfs(S,inf);
}return re;
}
void draw(int x,int y){
int p=id(x,y),w;
scanf("%d",&w); tot+=w;
if((x+y)&){//不要重复连边
link(S,p,w);
for(int i=;i<;++i){
int r1=x+d1[i],r2=y+d2[i];
if(r1>&&r1<=n&&r2>&&r2<=m)
link(p,id(r1,r2),inf);
}
}else link(p,T,w);
}
int main(){
scanf("%d%d",&n,&m);
S=n*m+; T=S+;
for(int i=;i<=n;++i)
for(int j=;j<=m;++j)
draw(i,j);
printf("%d",tot-dinic());
return ;
}
05-02 23:40