【题解】P4003 无限之环

前言
我嘞个费用流啊,我勒个建模好题啊

题意

link

在这里插入图片描述

简述:
在一个 n × m n\times m n×m 的网格图内有一些水管,问能否拼成不漏水的形状。

题解

分析

看到方格图,按照套路,考虑对原图进行黑白染色。

然后看看这个图有什么用,考虑将源点连向所有黑点,所有白点连向汇点,然后直接跑最小费用最大流,如果 2 × 2\times 2× 最大流量 = = =接口数,那么解即为最小费用,否则无解。

因为每一个白点接口和每一个黑点接口匹配上,就可以产生 1 1 1 点贡献,所以总贡献因为接口总数的一半,否则就有接口匹配不上,就漏水了。

怎么建图呢?考虑拆点,将每一个点拆成上,下,左,右,中五个点,中点连向源点或汇点,其余四个点和异种颜色的这四类点匹配。

重中之重来了——怎么旋转。

以黑点为例(白点的边要反过来)

先考虑最简单的: 000 1 ( 2 ) 0001_{(2)} 0001(2)

如图:

考虑连一条红边,流量为 1 1 1,费用为 0 0 0,这是这个水管的基本数据。

那旋转呢?

考虑连蓝边,流量为 1 1 1,费用 > 0 >0 >0

如图:

蓝边第一个数为流量,第二个数为费用,这样我们就可以模拟旋转操作了。只需要计算出相应的费用即可。

再深入一点,我们用红边限制流量,用蓝边模拟旋转。

接下来是 T 型,如图:

这个各位读者可以思考一下为什么是连 3 3 3 条边,且费用为什么为那些数,最好模拟一下。

然后是 L 型,如图:

剩下的都差不多,不画了。

Code

参考的核心代码:

#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
#define cou(i) cout<<fixed<<setprecision(i)
using namespace std;
const int N=2e3+1,M=2e6+1,inf=1e9;
int n,m,k,s,t,o,ans,mi_c,idx=1,cnt=0;
int dis[M],h[M],vis[M],head[M],cur[M],ide;
struct fy{
	int v,w,co,nxt;
}edge[M<<1];
struct fy_{
	int id,dis;
	bool operator<(const fy_ &x)const{return x.dis<dis;}
};
void add(int x,int y,int z,int cost){
	edge[++idx].v=y,edge[idx].w=z,edge[idx].co=cost,edge[idx].nxt=head[x],head[x]=idx;
	edge[++idx].v=x,edge[idx].w=0,edge[idx].co=-cost,edge[idx].nxt=head[y],head[y]=idx;
}
void link(int x,int y,int z,int cost,int id){
	if(id)
		add(x,y,z,cost);
	else
		add(y,x,z,cost);
}
void dinic(){
	//这个就不放了
}
int getmid(int x,int y){
	return (x-1)*m+y;
}
int getlid(int x,int y){
	return (x-1)*m+y+n*m;
}
int getrid(int x,int y){
	return (x-1)*m+y+2*n*m;
}
int getuid(int x,int y){
	return (x-1)*m+y+3*n*m;
}
int getdid(int x,int y){
	return (x-1)*m+y+4*n*m;
}
inline bool check(int i,int j){
	if(i<1||j<1||i>n||j>m)
		return 0;
	return 1;
}
signed main(){
	IOS;
	cou(0);
	cin>>n>>m;
	s=0,t=5*n*m+1;
	for(int i=1;i<=n;i++)
		for(int j=1,x;j<=m;j++){
			cin>>x;
			int A=i+j&1;
			int mid=getmid(i,j);
			int lid=getlid(i,j);
			int rid=getrid(i,j);
			int uid=getuid(i,j);
			int did=getdid(i,j);
			if(A){
				add(s,mid,inf,0);
				if(check(i,j-1))
					link(lid,getrid(i,j-1),1,0,A);
				if(check(i,j+1))
					link(rid,getlid(i,j+1),1,0,A);
				if(check(i-1,j))
					link(uid,getdid(i-1,j),1,0,A);
				if(check(i+1,j))
					link(did,getuid(i+1,j),1,0,A);
			}
			else
				add(mid,t,inf,0);
			bool L,R,U,D;
			L=R=U=D=0;
			if(x&1)
				link(mid,uid,1,0,A),cnt++,U=1;
			if(x&2)
				link(mid,rid,1,0,A),cnt++,R=1;
			if(x&4)
				link(mid,did,1,0,A),cnt++,D=1;
			if(x&8)
				link(mid,lid,1,0,A),cnt++,L=1;
			
			if(x==1){
				link(uid,lid,1,1,A);
				link(uid,rid,1,1,A);
				link(uid,did,1,2,A);
				continue;
			}
			if(x==2){
				link(rid,uid,1,1,A);
				link(rid,did,1,1,A);
				link(rid,lid,1,2,A);
				continue;
			}
			if(x==4){
				link(did,lid,1,1,A);
				link(did,rid,1,1,A);
				link(did,uid,1,2,A);
				continue;
			}
			if(x==8){
				link(lid,uid,1,1,A);
				link(lid,did,1,1,A);
				link(lid,rid,1,2,A);
				continue;
			}
			
			if(L&&R&&U&&D)
				continue;
			
			if(L&&R&&U){
				link(lid,did,1,1,A);
				link(rid,did,1,1,A);
				link(uid,did,1,2,A);
				continue;
			}
			if(L&&R&&D){
				link(lid,uid,1,1,A);
				link(rid,uid,1,1,A);
				link(did,uid,1,2,A);
				continue;
			}
			if(U&&D&&L){
				link(uid,rid,1,1,A);
				link(did,rid,1,1,A);
				link(lid,rid,1,2,A);
				continue;
			}
			if(U&&D&&R){
				link(uid,lid,1,1,A);
				link(did,lid,1,1,A);
				link(rid,lid,1,2,A);
				continue;
			}
			
			if(U&&R){
				link(uid,did,1,1,A);
				link(rid,lid,1,1,A);
			}
			if(U&&L){
				link(uid,did,1,1,A);
				link(lid,rid,1,1,A);
			}
			if(D&&R){
				link(did,uid,1,1,A);
				link(rid,lid,1,1,A);
			}
			if(D&&L){
				link(did,uid,1,1,A);
				link(lid,rid,1,1,A);
			}
		}
	dinic();
	if(ans*2!=cnt)
		mi_c=-1;
	cout<<mi_c<<"\n";
	return 0;
}
  • 11
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值