POI2010 Bridges

白昼怎懂夜的黑 提交于 2019-11-27 08:12:05

问题描述

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\n",ans);
    return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!