【[网络流24题]方格取数问题】
发现这个图还挺不好连得
因为并不是非常经典的最大权闭合子图了
试图拆点也发现没有什么效果
但是转念一想发现最大权闭合子图之所以能跑最小割就是因为我们强行分组了
这里是不是也可以强行分组啊
于是我们可以强行分组,把所有点分成黑点和白点,黑点向\(T\)连边,\(S\)向白点连边,边权是这个点的权值
之后白点向他能控制的黑点连边就行了
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define maxn 100005
#define re register
#define LL long long
#define inf 999999999
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
const int dx[]={0,0,1,-1};
const int dy[]={1,-1,0,0};
inline int read()
{
char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();
return x;
}
struct E{int v,nxt,w,f;}e[maxn<<3];
int d[maxn],head[maxn],cur[maxn];
int a[101][101],b[101][101],c[101][101];
int n,m,num=1,S,T,tot,ans;
inline void add(int x,int y,int z) {e[++num].v=y;e[num].w=z;e[num].nxt=head[x];head[x]=num;}
inline void C(int x,int y,int z) {add(x,y,z),add(y,x,0);}
inline int BFS()
{
std::queue<int> q;
for(re int i=S;i<=T;i++) cur[i]=head[i],d[i]=0;
d[S]=1,q.push(S);
while(!q.empty())
{
int k=q.front();q.pop();
for(re int i=head[k];i;i=e[i].nxt)
if(!d[e[i].v]&&e[i].w>e[i].f) d[e[i].v]=d[k]+1,q.push(e[i].v);
}
return d[T];
}
int dfs(int x,int now)
{
if(x==T||!now) return now;
int flow=0,ff;
for(re int& i=cur[x];i;i=e[i].nxt)
if(d[e[i].v]==d[x]+1)
{
ff=dfs(e[i].v,min(now,e[i].w-e[i].f));
if(ff<=0) continue;
now-=ff,flow+=ff;
e[i].f+=ff,e[i^1].f-=ff;
if(!now) break;
}
return flow;
}
int main()
{
n=read(),m=read();
for(re int i=1;i<=n;i++)
for(re int j=1;j<=m;j++)
a[i][j]=read(),b[i][j]=++tot,ans+=a[i][j];
for(re int i=1;i<=n;i++)
for(re int j=1;j<=m;j++) c[i][j]=(i+j)%2;
T=++tot;
for(re int i=1;i<=n;i++)
for(re int j=1;j<=m;j++)
if(c[i][j]) C(S,b[i][j],a[i][j]);else C(b[i][j],T,a[i][j]);
for(re int i=1;i<=n;i++)
for(re int j=1;j<=m;j++)
for(re int k=0;c[i][j]&&k<4;k++)
{
int xx=i+dx[k],yy=j+dy[k];
if(xx>=1&&yy>=1&&xx<=n&&yy<=m) if(!c[xx][yy]) C(b[i][j],b[xx][yy],inf);
}
while(BFS()) ans-=dfs(S,inf);
printf("%d\n",ans);
return 0;
}