• 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。

    输出格式

    输出:如果无法完成减肥计划,则输出NIE,否则第一行输出承受风力的最大值(要使它最小)

    样例输入

    4 4
    1 2 2 4
    2 3 3 4
    3 4 4 4
    4 1 5 4

    样例输出

    4

    提示

    注意:通过桥为欧拉回路

    解析

    题目要求最大承受风力的最小值,那很明显是二分答案。

    二分答案后这个图就会变成一个混合图(也就是既有单向边也有双向边的)。然后只需要判断这个混合图是否为欧拉回路即可。

    具体的我们使用网络流模型。设d[i]表示一个点入度减出度的值。对于一条有向边(u,v),就直接d[u]--,d[v]++(即u出度加1,v入度加1)。对于一条无向边(u,v),先假设方向为u->v。如果变向,则会导致d[u]+=2,d[v]-=2。最后,如果是一条欧拉回路,则每一个d[i]都等于0。所以,我们通过改变无向边的方向使任意d[i]等于0。对于d[i]>0,从原点向i连一条容量为d[i]/2的边;对于d[i]<0,向汇点连一条容量为-d[i]/2的边。对于无向边,若当前方向为u到v,则从v向u连一条容量为1的边表示变向。最后如果满流说明是欧拉回路。

    对于这道题,二分一个mid,每一条边如果有一个小于等于mid,就作为单向边,两个就作为无向边。建图跑Dinic判断。

    代码

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    #define N 1002
    #define M 2002
    #define S 0
    #define T n+1
    using namespace std;
    const int inf=1<<30;
    struct edge{
    	int u,v,a,b;
    }e[M];
    int head[N],ver[M*10],nxt[M*10],cap[M*10],l;
    int n,m,i,d[N],dis[N];
    int read()
    {
    	char c=getchar();
    	int w=0;
    	while(c<'0'||c>'9') c=getchar();
    	while(c<='9'&&c>='0'){
    		w=w*10+c-'0';
    		c=getchar();
    	}
    	return w;
    }
    int abs(int x)
    {
    	return x<0?-x:x;
    }
    void insert(int x,int y,int z)
    {
    	ver[l]=y;
    	cap[l]=z;
    	nxt[l]=head[x];
    	head[x]=l;
    	l++;
    	ver[l]=x;
    	cap[l]=0;
    	nxt[l]=head[y];
    	head[y]=l;
    	l++;
    }
    bool bfs()
    {
    	queue<int> q;
    	memset(dis,-1,sizeof(dis));
    	q.push(S);
    	dis[S]=0;
    	while(!q.empty()){
    		int x=q.front();
    		q.pop();
    		for(int i=head[x];i!=-1;i=nxt[i]){
    			int y=ver[i];
    			if(dis[y]<0&&cap[i]>0){
    				dis[y]=dis[x]+1;
    				q.push(y);
    			}
    		}
    	}
    	return (dis[T]>0);
    }
    int Dinic(int x,int flow)
    {
    	if(x==T||flow==0) return flow;
    	int ans=0;
    	for(int i=head[x];i!=-1;i=nxt[i]){
    		int y=ver[i];
    		if(dis[y]==dis[x]+1&&cap[i]>0){
    			int a=Dinic(y,min(flow,cap[i]));
    			flow-=a;
    			ans+=a;
    			cap[i]-=a;
    			cap[i^1]+=a;
    		}
    		if(flow==0) break;
    	}
    	if(flow) dis[x]=-1;
    	return ans;
    }
    bool check(int x)
    {
    	memset(head,-1,sizeof(head));
    	memset(d,0,sizeof(d));
    	l=0;
    	int ans=0,sum=0;
    	for(int i=1;i<=m;i++){
    		if(e[i].a<=x) d[e[i].u]--,d[e[i].v]++;
    		if(e[i].b<=x) insert(e[i].v,e[i].u,1);
    	}
    	for(int i=1;i<=n;i++){
    		if(abs(d[i])%2==1) return 0;
    		if(d[i]<0) insert(i,T,-d[i]/2);
    		else insert(S,i,d[i]/2),sum+=d[i]/2;
    	}
    	while(bfs()) ans+=Dinic(S,inf);
    	return (ans==sum);
    }
    int main()
    {
    	int l=inf,r=-inf,mid,ans=-1;
    	n=read();m=read();
    	for(i=1;i<=m;i++){
    		e[i].u=read();e[i].v=read();
    		e[i].a=read();e[i].b=read();
    		if(e[i].a>e[i].b){
    			swap(e[i].u,e[i].v);
    			swap(e[i].a,e[i].b);
    		}
    		l=min(l,e[i].a);
    		r=max(r,e[i].b);
    	}
    	while(l<=r){
    		mid=(l+r)/2;
    		if(check(mid)){
    			ans=mid;
    			r=mid-1;
    		}
    		else l=mid+1;
    	}
    	if(ans==-1) puts("NIE");
    	else printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    搜索引擎的变化 【转载】
    转贴:Apache重负荷服务器应如何优化
    开源WebGIS系统构建工具集合
    linux命令大全
    英语新闻常用搜索引擎 【转载】
    转载王垠的《完全用GNU/Linux工作》!!!
    国内外搜索引擎论坛 【转载】
    刚刚申请的WebGIS应用QQ群,最大用户80,欢迎加入啊!
    转贴:用Apache反向代理设置对外的WWW和文件服务器
    sourceforge.net上的GeoServer的下载地址列表
  • 原文地址:https://www.cnblogs.com/LSlzf/p/11674973.html
Copyright © 2020-2023  润新知