bzoj2923/洛谷P3756 老C的方块 最小割

本文深入探讨了如何通过网格图的红白染色转换,将特定问题转化为最小割问题,详细介绍了建图过程和算法实现,包括节点间的依赖关系和边的流量设定。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目分析

先将网格图红白染色(你问为什么不是黑白?因为我在草稿纸上画图时发现把黑色格子改成红色是不可能的……)。

然后将与蓝色线相邻的红色格子改成黑色,相邻的白色格子改成蓝色。

大概长这样(灵魂画手litble再现江湖!)

灵魂画手litble

这样你会发现:

1.整张图黑格子与红格不相邻,蓝格子和白格子不相邻。

2.任何一个特殊图形,是一对相邻的黑格子和蓝格子,加上与它们连通的一个白格子和一个蓝格子。

思考建图,由这些格子之间的依赖关系可以猜到是个最小割。

假设有一个蓝格子A和与其相邻的一个黑格子B,与他们直接相连的白色和红色格子都有方块,那么我们需要选择下列三种操作的至少一种:

1.删掉所有白格子

2.删掉所有红格子

3.删掉A和B其中之一

因此得到建图:S像所有红格子连一条流量为红格子费用的边,红格子向相邻有方块的蓝格子连一条流量为inf的边,蓝格子向相邻黑格子连一条流量为min(蓝格子费用,黑格子费用)的边,黑格子向相邻白格子连一条流量为inf的边,白格子向T连一条流量为白格子费用的边。

最小割即为答案。

代码

#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
	int q=0;char ch=' ';
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
	return q;
}
typedef long long LL;
const int N=100010,inf=0x3f3f3f3f;
int n,m,K,cnt,S,T,ans,tot=1;
map<LL,int> mp;
int X[N],Y[N],Z[N],col[N];
int lev[N],q[N],h[N<<3],ne[N<<3],flow[N<<3],to[N<<3];
int mvx[5]={1,-1,0,0},mvy[5]={0,0,1,-1};

LL id(LL x,LL y) {return (x-1)*m+y;}
void add(int x,int y,int z) {
	to[++tot]=y,ne[tot]=h[x],h[x]=tot,flow[tot]=z;
	to[++tot]=x,ne[tot]=h[y],h[y]=tot,flow[tot]=0;
}
int dfs(int x,int liu) {
	if(x==T) return liu;
	int sum=0;
	for(RI i=h[x];i;i=ne[i])
		if(flow[i]>0&&lev[to[i]]==lev[x]+1) {
			int kl=dfs(to[i],min(flow[i],liu-sum));
			sum+=kl,flow[i]-=kl,flow[i^1]+=kl;
			if(sum==liu) return sum;
		}
	if(!sum) lev[x]=-1;
	return sum;
}
int bfs() {
	for(RI i=1;i<=cnt;++i) lev[i]=0;
	int he=1,ta=1;q[1]=S,lev[S]=1;
	while(he<=ta) {
		int x=q[he];++he;
		if(x==T) return 1;
		for(RI i=h[x];i;i=ne[i])
			if(flow[i]>0&&!lev[to[i]])
				lev[to[i]]=lev[x]+1,q[++ta]=to[i];
	}
	return 0;
}

void work_black(int x,int y,int k1) {
	for(RI i=0;i<4;++i) {
		int tx=x+mvx[i],ty=y+mvy[i];
		if(tx>=1&&tx<=n&&ty>=1&&ty<=m&&mp[id(tx,ty)]) {
			int k2=mp[id(tx,ty)];
			if(col[k2]==3) add(k2,k1,min(Z[k1],Z[k2]));
			else add(k1,k2,inf);
		}
	}
}
void work_blue(int x,int y,int k1) {
	for(RI i=0;i<4;++i) {
		int tx=x+mvx[i],ty=y+mvy[i];
		if(tx>=1&&tx<=n&&ty>=1&&ty<=m&&mp[id(tx,ty)]) {
			int k2=mp[id(tx,ty)];
			if(col[k2]!=2) add(k2,k1,inf);
		}
	}
}
int main()
{
	n=read(),m=read(),K=read();
	for(RI i=1;i<=K;++i)
		X[i]=read(),Y[i]=read(),Z[i]=read(),mp[id(X[i],Y[i])]=++cnt;
	S=++cnt,T=++cnt;
	for(RI i=1;i<=K;++i) {
		if(X[i]%4==1) col[i]=(Y[i]&1?2:4);
		else if(X[i]%4==2) col[i]=(Y[i]&1?3:1);
		else if(X[i]%4==3) col[i]=(Y[i]&1?1:3);
		else col[i]=(Y[i]&1?4:2);
	}
	for(RI i=1;i<=K;++i) {
		if(col[i]==2) work_black(X[i],Y[i],i);
		else if(col[i]==3) work_blue(X[i],Y[i],i);
		else if(col[i]==1) add(S,i,Z[i]);
		else add(i,T,Z[i]);
	}
	while(bfs()) ans+=dfs(S,inf);
	printf("%d\n",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值