bzoj2095 bridges 【网络流判欧拉回路】

题意:
给出一个n个点m条边的无向图,每个边有一正一反两个权值;
现要从点1出发,对每条边经过且仅经过一次;
求一种方案使经过的最大权值最小;
(bzoj)输出这个权值即可;

题解:
最小值最大显然二分;
二分之后就转化成了一个判定性问题:求这个图中是否存在欧拉回路;
而最糟糕的是。。这是混合图。。。
有向图的欧拉回路:每个点的入度=出度
无向图的欧拉回路:每个点的度为偶数;
混合图的欧拉回路:网络流!
混合图与有向图的区别就在于其中无向边的方向不一定;

我们先使所有的无向边固定一个方向;
很明显,每个点的度一定要是偶数。
但这样得到的图未必是一个欧拉图,因为有的点入度大于出度,有的点出度大于入度;
所以我们需将其调整为每个点的出度等于入度。那么每个点最终出度入度=(当前出度+入度)/2,然后经计算可得每个点需调整 |(入度-出度)/2| 的边,但由于调整一个点会影响到另一个点,所以想到了网络流。
为了调整这里,我们:
从源点向入度大于出度的点连流量为入度减出度/2的边;
从入度小于出度向汇点的点连流量为出度减入度/2的边;
对一条无向边,连这条边的方向反向,流量为1的边;
每流过一条边,相当于将这条边反向,两个点的入度与出度得到调整;
对这个网络求最大流就调整了尽可能多的无向边;
检查是否存在欧拉回路就是查源点汇点所连的边是否满流即可;
注意还有一条件是图连通,用并查集判断一下就好了;
时间复杂度O(Dinic*logm);

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;

int getint()
{
    int i=0;char c;
    for(c=getchar();c>'9'||c<'0';c=getchar());
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i;
}

const int N=1005,M=8005,INF=0x3f3f3f3f;
int n,m,src,des,tot,totin,totout;
int X[M],Y[M],a[M],b[M],que[N];
int in[N],out[N],fa[N],size[N],dis[N];
int first[N],cur[N],next[M],to[M],cap[M];

void add(int x,int y,int z)
{
    next[++tot]=first[x],first[x]=tot,to[tot]=y,cap[tot]=z;
    next[++tot]=first[y],first[y]=tot,to[tot]=x,cap[tot]=0;
}

int find(int x)
{
    return fa[x]==x?x:fa[x]=find(fa[x]);
}

void merge(int x,int y)
{
    x=find(x),y=find(y);
    if(x!=y)fa[y]=x,size[x]+=size[y];
}

bool bfs()
{
    for(int i=1;i<=des;i++)dis[i]=-1,cur[i]=first[i];
    int head=0,tail=0;
    que[++tail]=src,dis[src]=0;
    while(head<tail)
    {
        int u=que[++head];
        for(int e=first[u];e;e=next[e])
        {
            int v=to[e];
            if(dis[v]==-1&&cap[e])
            {
                dis[v]=dis[u]+1;
                que[++tail]=v;
                if(v==des)return 1; 
            }
        } 
    }
    return 0;
}

int dinic(int u,int flow)
{
    if(u==des)return flow;
    int res=0;
    for(int &e=cur[u];e;e=next[e])
    {
        int v=to[e];
        if(cap[e]&&dis[v]==dis[u]+1)
        {
            int delta=dinic(v,min(flow-res,cap[e]));
            cap[e]-=delta,cap[e^1]+=delta;
            res+=delta;if(res==flow)break;
        }
    }
    if(res<flow)dis[u]==-1;
    return res;
}

int maxflow()
{
    int res=0;
    while(bfs())res+=dinic(src,INF);
    return res;
}

bool check(int lim)
{
    memset(in,0,sizeof(in));
    memset(out,0,sizeof(out));
    memset(first,0,sizeof(first));
    tot=1,totin=totout=0;
    for(int i=1;i<=n;i++)fa[i]=i,size[i]=1;
    for(int i=1;i<=m;i++)
    {
        if(a[i]<=lim&&b[i]<=lim)
        {
            out[X[i]]++,in[Y[i]]++;
            merge(X[i],Y[i]);
            add(Y[i],X[i],1);
        }
        else if(a[i]<=lim)
        {
            out[X[i]]++,in[Y[i]]++;
            merge(X[i],Y[i]);
        }
        else if(b[i]<=lim)
        {
            out[Y[i]]++,in[X[i]]++;
            merge(X[i],Y[i]);
        }
    }
    if(size[find(1)]!=n)return 0;
    for(int i=1;i<=n;i++)
    {
        if((in[i]+out[i])&1)return 0;
        if(in[i]==out[i])continue;
        if(in[i]>out[i])
        {
            add(src,i,in[i]-out[i]>>1);
            totin+=in[i]-out[i]>>1;
        }
        else
        {
            add(i,des,out[i]-in[i]>>1);
            totout+=out[i]-in[i]>>1;
        }
    }
    if(totin!=totout)return 0;
    if(maxflow()==totin)return 1;
    else return 0;

}


int main()
{
    //freopen("lx.in","r",stdin);
    int l,r,mi,mx;
    n=getint(),m=getint();
    src=n+1,des=n+2;
    for(int i=1;i<=m;i++)
    {
        X[i]=getint(),Y[i]=getint();
        a[i]=getint(),b[i]=getint();
        mi=min(mi,min(a[i],b[i]));
        mx=max(mx,max(a[i],b[i]));
    }
    l=mi,r=mx;
    while(l<=r)
    {
        int mid=l+r>>1;
        if(check(mid))r=mid-1;
        else l=mid+1;
    }
    if(l>mx)cout<<"NIE"<<'\n';
    else cout<<l<<'\n';
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值