無限之環

無限之環

題解

最開始看上去像道模擬,不過看到數據範圍就知道不可能了。好吧,正解是網絡流,接下來講一下網絡流怎麼打這一道題。

我們知道,對於每一個格子的每一個管子,它一定會與附近的一個格子的一個管子相連,這是毋庸置疑的。我們必須要讓每個管子都流滿否則必定會漏水。

我們發現,整個圖最後一定會變成若干個獨立的聯通塊,我們可以將每個格子都連向源點或者匯點,至於怎麼連可以通過黑白染色的方法來判斷。

對於產生的費用這個最重要的花費,我們可以通過旋轉與拆點來表示。一個格子需要拆成五個點,分別代表上下左右中。旋轉邊的花費我們只需要在內部依次向中心連花費爲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;
}

謝謝!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章