1. 程式人生 > >POI2010 Mos-Bridges(二分答案+歐拉回路+網路流)

POI2010 Mos-Bridges(二分答案+歐拉回路+網路流)

【題目描述】

YYD 為了減肥,他來到了瘦海,這是一個巨大的海,海中有 n nn 個小島,小島之間有 m mm 座橋連線,兩個小島之間不會有兩座橋,並且從一個小島可以到另外任意一個小島。現在 YYD 想騎單車從小島 1 11 出發,騎過每一座橋,到達每一個小島,然後回到小島 1 11。霸中同學為了讓 YYD 減肥成功,召喚了大風,由於是海上,風變得十分大,經過每一座橋都有不可避免的風阻礙 YYD,YYD 十分 ddt,於是用泡芙賄賂了你,希望你能幫他找出一條承受的最大風力最小的路線。

【輸入格式】

第一行為兩個用空格隔開的整數n(2<=n<=1000),m(1<=m<=2000),接下來讀入m行由空格隔開的4個整數a,b(1<=a,b<=n,a<>b),c,d(1<=c,d<=1000),表示第i+1行第i座橋連線小島a和b,從a到b承受的風力為c,從b到a承受的風力為d。

【輸出格式】

如果無法完成減肥計劃,則輸出 NIE,否則第一行輸出承受風力的最大值(要使它最小)

【樣例輸入】

4 4

1 2 2 4

2 3 3 4

3 4 4 4

4 1 5 4

【樣例輸出】

4

【備註】

注意:通過橋為歐拉回路

【題目分析】

要讓最大值最小,顯然是二分答案的套路,所以我們就去二分這個最大值,將所有不大於這個值的邊加入圖中,然後檢查是否構成歐拉回路。

然後問題也就來了,單純的無向圖和有向圖的歐拉回路好判,但如何在混合圖中去判歐拉回路?

VANVAN沒想到就是網路流啊。。。。。。

首先我們將所有無向邊任意定向,統計一下所有點出入度數,如果二者奇偶性不同,那麼無解(將一條無向邊反向後出入度變化2),所以每個點向源匯點連一條自身出入度差值的一半的邊,表示需要轉向這麼多次。

那麼問題就轉化為匹配問題,如果最後最大流流滿,那麼就一定對應原圖中存在歐拉回路。

【程式碼~】

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN=1e3+10;
const int MAXM=4e3+10;
const int MAXP=1e6+10;
const int INF=0x3f3f3f3f;

int Read()
{
	int i=0,f=1;
	char c;
	for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
	if(c=='-')
	  f=-1,c=getchar();
	for(;c>='0'&&c<='9';c=getchar())
	  i=(i<<3)+(i<<1)+c-'0';
	return i*f;
}

int n,m,cnt;
int s=0,t=1001;
int minn=INF,maxx=0;
int head[MAXN],depth[MAXN],cur[MAXN];
int nxt[MAXP],to[MAXP],w[MAXP];
int from[MAXM],too[MAXM],w1[MAXM],w2[MAXM];
int du[MAXN],tot;

void Add(int x,int y,int z)
{
	nxt[cnt]=head[x];
	head[x]=cnt;
	to[cnt]=y;
	w[cnt]=z;
	cnt++;
}

void add(int x,int y,int z)
{
	Add(x,y,z);
	Add(y,x,0);
}

bool bfs(){
	memset(depth,0,sizeof(depth));
	queue<int> q;
	q.push(s);
	depth[s]=1;
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		for(int i=head[u];i!=-1;i=nxt[i])
		{
			int v=to[i];
			if(w[i]&&depth[v]==0)
			{
				depth[v]=depth[u]+1;
				q.push(v);
			}
		}
	}
	if(depth[t]==0)
	  return false;
	return true;
}

int dfs(int u,int dist)
{
	if(u==t)
	  return dist;
	for(int &i=cur[u];i!=-1;i=nxt[i])
	{
		int v=to[i];
		if(w[i]&&depth[v]==depth[u]+1)
		{
			int di=dfs(v,min(dist,w[i]));
			if(di>0)
			{
				w[i]-=di;
				w[i^1]+=di;
				return di;
			}
		}
	}
	return 0;
}

int dinic()
{
	int ans=0;
	while(bfs())
	{
		for(int i=s;i<=t;++i)
		  cur[i]=head[i];
		while(int d=dfs(s,INF))
		  ans+=d;
	}
	return ans;
}

void build(int mid)
{
	memset(head,-1,sizeof(head));
	memset(du,0,sizeof(du));
	cnt=0;
	tot=0;
	for(int i=1;i<=m;++i)
	{
		if(w1[i]<=mid)
		  --du[from[i]],++du[too[i]];
		if(w2[i]<=mid)
		  add(too[i],from[i],1);
	}
	for(int i=1;i<=n;++i)
	  if(du[i]>0)
	    tot+=du[i]>>1,add(s,i,du[i]>>1);
	  else 
	    if(du[i]<0)
		  add(i,t,(-du[i])>>1);
}

bool check(int mid)
{
	build(mid);
	for(int i=1;i<=n;++i)
	  if(du[i]&1)
	    return false;
	if(dinic()==tot)
	  return true;
	return false;
}

int main()
{
	n=Read(),m=Read();
	for(int i=1;i<=m;++i)
	{
		from[i]=Read(),too[i]=Read(),w1[i]=Read(),w2[i]=Read();
		if(w1[i]>w2[i])
		  swap(w1[i],w2[i]),swap(from[i],too[i]);
		minn=min(minn,w1[i]);
		maxx=max(maxx,w2[i]);
	}
	int l=minn,r=maxx;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(check(mid))
		  r=mid-1;
		else 
		  l=mid+1;
	}
	if(l==maxx+1)
	  puts("NIE");
	else 
	  cout<<l;
	return 0;
}