洛谷2774 转换成二分图用网络流

博客介绍了如何将寻找相邻方块最小值的问题转化为求解二分图的最小割问题,通过建立二分图模型,寻找最大流来实现。作者强调了将问题转换为二分图的思想对于解决此类问题的重要性,并简述了二分图最大匹配的含义。

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

网络流24题呀,然后就使劲的各种建图搞,但发现还是搞不了。始终不能解决的问题就是,会出现相邻的方块,但是他们都被选了。 

还是太弱鸡了,这题是将整个图染成黑白相间的颜色。拿着整个图值的总和 - 我们最终扣掉的那些方格的值。所以就转换成求最小的扣掉的那些方格的值。建立二分图,左边是白方格集合X,右边是黑方格集合Y。然后在相邻的方格之间建边,也就是在X和Y之间建边。这个时候跑出的最大流就是最小割。 

这样求解为啥对呢?我们需要选择一些的相邻的点,使得他们的值的和最小,并且去掉他们之后剩下的点都是不相邻的。求出最小割,就是使得剩下点不存在相邻的边,并且剩下点的值尽量的大。

感觉值得学习是这个转换成二分图的思想,另外二分图的最大匹配通俗一点来说就是X点集合和Y点集合之间不会再存在多余的点相互之间有联系,因为已经尽可能的进行匹配了。

#include <bits/stdc++.h>
using namespace std;
#define ll long long 
#define inf 0x3f3f3f3f
#define llinf 1e17
const int maxn=105;
int n,m,g[maxn][maxn];
int ans=2,head[maxn*maxn*2],S,T;
int deep[maxn*maxn*2],que[maxn*maxn*2],cur[maxn*maxn*2];
bool isblack[maxn][maxn];
struct edge{
	int to,next,v;
}e[100000];
void add(int x,int y,int v){
	e[ans].to=y; e[ans].next=head[x]; e[ans].v=v;
	head[x]=ans++;
	e[ans].to=x; e[ans].next=head[y]; e[ans].v=0;
	head[y]=ans++;
}

bool bfs(int S,int T)
{
	for(int i=0;i<=n;i++) cur[i]=head[i];//当前弧优化 
	memset(deep,0,sizeof(deep));
	int l=1,r=1;
	deep[S]=1;
	que[1]=S;
	while(l<=r){
		int x=que[l];
		for(int i=head[x];i;i=e[i].next){
			int y=e[i].to,v=e[i].v;
			if(!deep[y]&&v){//易忽略边不为0 
				deep[y]=deep[x]+1;
				que[++r]=y;
			}
		}
		l++;
	}
	if(deep[T]) return true;
	else return false;
}

int dfs(int x,int dist){
	if(x==T||0==dist) return dist;
	int res=0,tp;
	for(int i=cur[x];i;i=e[i].next){ 
		cur[x]=i;//当前弧优化 
		int y=e[i].to;
		if(deep[y]==deep[x]+1&&e[i].v){//易忽略边不为0 
			tp=dfs(y,min(dist,e[i].v));
			e[i].v-=tp;
			e[i^1].v+=tp;
			res+=tp;
			dist-=tp;
			if(0==dist) break;			
		}
	}
	if(!res) deep[x]=0;//这个优化真是。。。加上效果太明显了 
	return res;
}


bool isin(int x,int y){
	if(x>n||y>m||x<1||y<1) return false;
	else return true;
} 

int main()
{
	scanf("%d%d",&n,&m);
	int sum=0;
	for(int i=1;i<=n;i++)	for(int j=1;j<=m;j++)
		scanf("%d",&g[i][j]),sum+=g[i][j];
	S=0,T=n*m*2+1;
	bool salix=true;
	for(int i=1;i<=n;i++){
		int flag;
		if(salix) flag=true;
		else flag=false;
		for(int j=1;j<=m;j++){
			if(flag) isblack[i][j]=true;
			else isblack[i][j]=false; 
			flag=!flag;
		}
		salix=!salix;
	}
	for(int i=1;i<=n*m;i++){
		int x=i/m,y=i-i/m*m;
		if(i%m) x++;
		if(0==y) y=m;
		add(i,i+n*m,g[x][y]);
		
		if(isblack[x][y]) add(S,i,inf);
		else add(i+n*m,T,inf);
	}
	for(int i=1;i<=n*m;i++){
		int x=i/m,y=i-i/m*m;
		if(i%m) x++;
		if(0==y) y=m;
		if(isin(x,y+1)){
			if(isblack[x][y]) add(i+n*m,i+1,inf);
			else add(i+1+n*m,i,inf);
		} 
		if(isin(x+1,y)){
			if(isblack[x][y]) add(i+n*m,i+m,inf);
			else add(i+m+n*m,i,inf);
		} 
	}
		
	n=n*m*2+1;
	int ans=0;
	while(bfs(S,T)){
		ans+=dfs(S,inf); 
	} 
	printf("%d",sum-ans);	
	return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值