前言
我嘞个费用流啊,我勒个建模好题啊
题意
简述:
在一个
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;
}