无限之环

无限之环

题解

最开始看上去像道模拟,不过看到数据范围就知道不可能了。好吧,正解是网络流,接下来讲一下网络流怎么打这一道题。

我们知道,对于每一个格子的每一个管子,它一定会与附近的一个格子的一个管子相连,这是毋庸置疑的。我们必须要让每个管子都流满否则必定会漏水。

我们发现,整个图最后一定会变成若干个独立的联通块,我们可以将每个格子都连向源点或者汇点,至于怎么连可以通过黑白染色的方法来判断。

对于产生的费用这个最重要的花费,我们可以通过旋转与拆点来表示。一个格子需要拆成五个点,分别代表上下左右中。旋转边的花费我们只需要在内部依次向中心连花费为1的边即可。

不过对于每种不同情况的格子我们都需要分别讨论一下,有点麻烦。

这题主要是情况有点多,都需要特判一下,没其他的了。

源码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#define MAXN 400005
#define MAXM 20005
using namespace std;
#define UP(x) x+turn*n*m
#define RI(x) x+((turn+1)&3)*n*m
#define DO(x) x+((turn+2)&3)*n*m
#define LE(x) x+((turn+3)&3)*n*m
#define MD(x) x+(n*m<<2)
typedef long long LL;
#define gc() getchar()
typedef pair<int,int> pii;
const int INF=0x7f7f7f7f;
int tot,head[MAXM],summ,ans;
int from[MAXN],to[MAXN];
int nxt[MAXN],s1,t1,pre[MAXM];
int dis[MAXM],cap[MAXN],paid[MAXN];
bool vis[MAXM];
int n,m,k,totf;
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=gc();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=gc();}
	while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=gc();}
	x*=f;
} 
void addEdge(int u,int v,int flow,int cost){
	//printf("%d %d %d %d\n",u,v,flow,cost);
	from[++tot]=u;to[tot]=v;paid[tot]=cost;
	cap[tot]=flow;nxt[tot]=head[u];head[u]=tot;
}
void addedge(int u,int v,int flow,int cost,int tp){
	if(tp) u^=v,v^=u,u^=v;
//printf("%d %d %d %d\n",u,v,flow,cost);
	addEdge(u,v,flow,cost);addEdge(v,u,0,-cost);
}
int EK(){
	int res=0,sum=0;
	while(1){
		for(int i=0;i<=t1;i++) dis[i]=INF,vis[i]=0,pre[i]=-1; 
		queue<int> q;while(!q.empty()) q.pop();
		dis[s1]=0;vis[s1]=1;q.push(s1);
		while(!q.empty()){
			int now=q.front();q.pop();vis[now]=0;
			//printf("%d\n",now);
			for(int i=head[now];~i;i=nxt[i]){
				int v=to[i];//printf("%d-->%d %d %d %d\n",now,v,dis[now],cap[i],dis[v]);
				if(cap[i]>0&&dis[v]>dis[now]+paid[i]){
					//printf("%d-->%d\n",now,v);
					dis[v]=dis[now]+paid[i];pre[v]=i;
					if(!vis[v]) q.push(v),vis[v]=1;
				}
			}
		}
		//printf("dis%d %d\n",res,dis[t1]);
		if(dis[t1]>INF-1) return totf==sum<<1?res:-1;int minn=INF;
		for(int i=pre[t1];~i;i=pre[from[i]])minn=min(minn,cap[i]);
		for(int i=pre[t1];~i;i=pre[from[i]])cap[i]-=minn,cap[i^1]+=minn;
		res+=dis[t1]*minn;sum+=minn;
		//printf("%d %d %d\n",res,minn,dis[t]);
	}
}
signed main() {
	tot=-1;memset(head,-1,sizeof(head));
	read(n);read(m);
	s1=0;t1=n*m*5+1;k=0,totf=0;
	for(int i=0;i<n;++i)
		for(int j=0;j<m;++j){
			int turn=0,t=(i+j)&1,shape;
			k++;//printf("%d %d %d\n",t,k,MD(k));
			if(t) addedge(s1,MD(k),INF,0,0);
			else addedge(MD(k),t1,INF,0,0);
			if(i) addedge(DO(k-m),UP(k),1,0,t);
			if(j) addedge(RI(k-1),LE(k),1,0,t);
			read(shape);
			if(shape&1)addedge(UP(k),MD(k),1,0,t),++totf;
            if(shape&2)addedge(RI(k),MD(k),1,0,t),++totf;
            if(shape&4)addedge(DO(k),MD(k),1,0,t),++totf;
            if(shape&8)addedge(LE(k),MD(k),1,0,t),++totf;
            switch(shape){
	            case 8:++turn;
	            case 4:++turn;
	            case 2:++turn;
	            case 1:
	                addedge(RI(k),UP(k),1,1,t);
	                addedge(DO(k),UP(k),1,2,t);
	                addedge(LE(k),UP(k),1,1,t);
	                break;
	            case 9:++turn;
	            case 12:++turn;
	            case 6:++turn;
	            case 3:       
	                addedge(DO(k),UP(k),1,1,t);
	                addedge(LE(k),RI(k),1,1,t);
	                break;
	            case 13:++turn;
	            case 14:++turn;
	            case 7:++turn; 
	            case 11:      
	                addedge(DO(k),LE(k),1,1,t);
	                addedge(DO(k),UP(k),1,2,t);
	                addedge(DO(k),RI(k),1,1,t);
	                break;
            }
		}
	printf("%d\n",EK());
    return 0;
}

谢谢!!!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值