BZOJ 2095: [Poi2010]Bridges 混合图欧拉回路

YYD为了减肥,他来到了瘦海,这是一个巨大的海,海中有n个小岛,小岛之间有m座桥连接,两个小岛之间不会有两座桥,并且从一个小岛可以到另外任意一个小岛。现在YYD想骑单车从小岛1出发,骑过每一座桥,到达每一个小岛,然后回到小岛1。霸中同学为了让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。
注意:通过桥为欧拉回路

二分答案之后只有小于等于mid的桥可以保留,有的是单向,有的是无向。
验证需要求无向图欧拉回路:
1:先检验每个点入度是否为偶数,这是最先的。
2:将每条无向边定向(随便定)。
3:每个点需要入度等于出度,但是随机定向不能满足。
出度大于入度的点,需要 出边改向数 - 入边改向数 = (outdrg - indrg) / 2
出度小于入度的点,需要 入边改向数 - 出边改向数 = (-outdrg + indrg) / 2
如果我们认为:1流量从定了向的无向边(也就是有向边)流往点A表示这条A的入边改向为出边,1流量从定了向的无向边(也就是有向边)流出点A表示这条A的出边改向为入边,那么根据流量守恒,出度大于入度的点需要从源点流入 (outdrg - indrg) / 2,而出度小于入度的点需要流向汇点(-outdrg + indrg) / 2,满流即有方案。

AC Code:

#include<bits/stdc++.h>
#define maxn 1005
#define maxm 10005
#define inf 0x3f3f3f3f
#define Clear(a,b) memset(a,b,sizeof a)
#define Copy(a,b) memcpy(a,b,sizeof a)
using namespace std;

int n,m,a[maxm],b[maxm],c[maxm],d[maxm];
int info[maxn],Prev[maxm],to[maxm],cap[maxm],cnt_e=1;
void Node(int u,int v,int c){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v,cap[cnt_e]=c; }
void Line(int u,int v,int c){ Node(u,v,c),Node(v,u,0); }
int S,T,h[maxn],g[maxn],buf[maxn],ot[maxn],vis[maxn],sb[maxn<<1];
void dfs(int u){ vis[u]=1; for(int i=info[u];i;i=Prev[i]) if(!vis[to[i]]) dfs(to[i]); }

int aug(int u,int mx){
	if(u == T) return mx;
	int st = mx , inc;
	for(int &i=info[u],v;i;i=Prev[i])
		if(cap[i] && h[v=to[i]]+1 == h[u] && (inc = aug(v,min(cap[i],st)))){
			cap[i] -= inc , cap[i^1] += inc;
			if(!(st-=inc) || h[0]) return mx-st;
		}
	if(!--g[h[u]]) h[0]=1;
	++g[++h[u]];info[u]=buf[u];
	return mx-st;
}

int main(){
	
	freopen("bridges.in","r",stdin);
	freopen("bridges.out","w",stdout);
	
	scanf("%d%d",&n,&m);S=n+1,T=n+2;
	int mx = 0;
	for(int i=1;i<=m;i++) scanf("%d%d%d%d",&a[i],&b[i],&c[i],&d[i]),mx=max(mx,min(c[i],d[i])),Line(a[i],b[i],0),ot[a[i]]++,ot[b[i]]++,sb[++sb[0]]=c[i],sb[++sb[0]]=d[i];
	dfs(1);
	for(int i=1;i<=n;i++) if(!vis[i] || (ot[i]&1)){
		puts("NIE");
		return 0;
	}
	sort(sb+1,sb+1+sb[0]),sb[0]=unique(sb+1,sb+1+sb[0])-sb-1;
	int L=lower_bound(sb+1,sb+1+sb[0],mx)-sb,R=sb[0],mid;
	for(;L<R;){
		mid=(L+R) >> 1;
		
		Clear(info,0),Clear(ot,0),cnt_e=1;
		for(int i=1;i<=m;i++) if(c[i]<=sb[mid]){
			ot[a[i]]++,ot[b[i]]--;
			if(d[i]<=sb[mid]) Line(a[i],b[i],1);
		}else ot[a[i]]--,ot[b[i]]++;
		int stm = 0;
		for(int i=1;i<=n;i++) if(ot[i]>0) Line(S,i,ot[i]/2),stm+=ot[i]/2;
		else if(ot[i]<0) Line(i,T,-ot[i]/2);
		for(Clear(h,0),Clear(g,0),Copy(buf,info);!h[0];) stm-=aug(S,inf);
		if(stm) L=mid+1;
		else R=mid;
	}
	printf("%d\n",sb[L]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值