【网络流24题】方格取数

原题走这里

似乎是非常经典的一道题

说白了就是二分图带权最大独立集

看到这道题的第一眼就想到了正确做法

然而一直不知道如何证明


黑白染色成二分图

源点向黑点连边,权值为格子上的数

白点向汇点连边,权值为格子上的数

黑点向相邻的白点连边,权值无穷


独立集与覆盖集互补,因而最大权独立集与最小权覆盖集互补

又对于覆盖集中的任意一个点,按照上述方法建图时,删去其与源/汇相连的边,就相当于删去了其在二分图中覆盖的边

因而最小权覆盖集等价于最小割,大致如此

即最大权独立集=总权值-最小权覆盖集=总权值-最小割=总权值-最大流,Dinic即可


代码如下:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
inline int read() {
	int X=0,w=0;
	char ch=0;
	while(!isdigit(ch)) {
		w|=ch=='-';
		ch=getchar();
	}
	while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
	return w?-X:X;
}
struct edge {
	int v,l,p;
} e[400010];
int n,m,top=1,head[1000],cur[1000],a[910],d[1000];
inline void addedge(int u,int v,int l) {
	e[++top]=(edge) {v,l,head[u]};
	head[u]=top;
	e[++top]=(edge) {u,0,head[v]};
	head[v]=top;
}
bool bfs() {
	memset(d,0,sizeof(d));
	queue<int> q;
	d[n*m+1]=1;
	q.push(n*m+1);
	while(!q.empty()) {
		int f=q.front();
		for(int i=head[f]; i; i=e[i].p) {
			if((!d[e[i].v])&&e[i].l) {
				d[e[i].v]=d[f]+1;
				q.push(e[i].v);
			}
		}
		q.pop();
	}
	return d[n*m+2];
}
int dfs(int u,int delta) {
	if(u==n*m+2) {
		return delta;
	}
	int ret=0;
	for(int &i=cur[u]; delta&&i; i=e[i].p) {
		if((d[e[i].v]==d[u]+1)&&e[i].l) {
			int dd=dfs(e[i].v,min(e[i].l,delta));
			e[i].l-=dd;
			e[i^1].l+=dd;
			delta-=dd;
			ret+=dd;
		}
	}
	if(!ret)cur[u]=0;
	return ret;
}
int Dinic() {
	int ret=0;
	while(bfs()) {
		memcpy(cur,head,sizeof(cur));
		ret+=dfs(n*m+1,INF);
	}
	return ret;
}
int main() {
	freopen("grid.in","r",stdin);
	freopen("grid.out","w",stdout);
	cin>>n>>m;
	int sum=0;
	for(int i=1; i<=n*m; i++) {
		cin>>a[i];
		sum+=a[i];
	}
	for(int i=1; i<=n*m; i++) {
		if(((i-1)%m+(i-1)/m)%2==0) {
			addedge(n*m+1,i,a[i]);
			if(i%m)addedge(i,i+1,INF);
			if(i%m!=1)addedge(i,i-1,INF);
			if(i>m)addedge(i,i-m,INF);
			if(i<=(n-1)*m)addedge(i,i+m,INF);
		} else {
			addedge(i,n*m+2,a[i]);
		}
	}
	cout<<sum-Dinic()<<endl;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值