[网络流24题] 方格取数问题

独自空忆成欢 提交于 2020-02-07 06:57:56

  相邻格不能同时选,通过黑白染色把相邻格染成不同色,构成一个二分图。

  由s向所有白点连接一条容量为wij的边,由黑点向t连接容量为wij的边,白点向相邻黑点点容量为INF的边,不能选相邻格即不能让s-t联通,问题转化为求s-t最小割,即最大流。

// q.c

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#define mem(a) memset(a,0,sizeof(a))
using namespace std;
const int M=35,INF=(int)1e8;
struct Edge {
	int v,nex,flow,cap; Edge() {}
	Edge(int a,int b,int c,int d):v(a),nex(b),flow(c),cap(d) {}
}ed[M*M<<4];
int cnt,head[M*M];
void add_edge(int a,int b,int c) {
	ed[cnt]=Edge(b,head[a],0,c); head[a]=cnt++;
	ed[cnt]=Edge(a,head[b],0,0); head[b]=cnt++;
}
struct Dinic {
	int n,s,t,dis[M*M],cur[M*M]; queue<int> Q;
	Dinic():n(0),s(0),t(0) {
		mem(dis); mem(cur);
		for(;!Q.empty();Q.pop());
	}
	bool bfs() {
		mem(dis); dis[s]=1; Q.push(s);
		int u,i; Edge e;
		while(!Q.empty()) {
			u=Q.front(); Q.pop();
			for(i=head[u];i!=-1;i=ed[i].nex) {
				e=ed[i];
				if(!dis[e.v]&&e.cap>e.flow) {
					dis[e.v]=dis[u]+1;
					Q.push(e.v);
				}
			}
		}
		return dis[t];
	}
	int dfs(int u,int lim) {
		if(u==t||!lim) return lim;
		int f=0,tmp=0; Edge e;
		for(int &i=cur[u];i!=-1;i=ed[i].nex) {
			e=ed[i];
			if(dis[e.v]==dis[u]+1) {
				f=dfs(e.v,min(lim,e.cap-e.flow));
				if(f>0) {
					ed[i].flow+=f; tmp+=f;
					ed[i^1].flow-=f; lim-=f;
					if(!lim) break;
				}
			}
		}
		return tmp;
	}
	int solve(int x,int y,int z) {
		s=x; t=y; n=z; int ans=0;
		for(;bfs();ans+=dfs(s,INF)) 
			for(int i=0;i<=n;i++) cur[i]=head[i];
		return ans;
	}
}DC;
int w[M][M],co[M][M],id[M][M];
int main() {
	freopen("grid.in","r",stdin);
	freopen("grid.out","w",stdout);
	memset(head,-1,sizeof(head));
	int m,n,sum=0,tot=0;
	scanf("%d%d",&m,&n); 
	for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) {
		scanf("%d",&w[i][j]); 
		sum+=w[i][j]; id[i][j]=++tot;
		(j==1)?(co[i][j]=co[i-1][j]^1):(co[i][j]=co[i][j-1]^1);
	}
	for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) {
		if(co[i][j]) {
			add_edge(0,id[i][j],w[i][j]);
			if(i-1>=1) add_edge(id[i][j],id[i-1][j],INF);
			if(j-1>=1) add_edge(id[i][j],id[i][j-1],INF);
			if(j+1<=n) add_edge(id[i][j],id[i][j+1],INF);
			if(i+1<=m) add_edge(id[i][j],id[i+1][j],INF);
		}
		else add_edge(id[i][j],tot+1,w[i][j]);
	}
	printf("%d\n",sum-DC.solve(0,tot+1,tot+1));
	return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!