BZOJ 2095 [Poi2010]Bridges 二分+最大流

题意:
给定一张图,每条边双向且双向都有权值,求一个从1开始的欧拉回路,使得回路上的边的最大权值最小。
解析:
最大值最小!!
二分!!二分!!
第一部分完成辣,二分出路上的最大权值。
然后这时候我们要处理下所有的边,如果边上权值大于我们二分的值的话显然是不可取的。
这其实就是个混合图的欧拉回路问题。
首先无向的边我们任意记录一下出度入度,然后有向边直接记录出度入度。
如果有一个点的出度入度之差是奇数那么显然不可以成为欧拉回路。
如果都是偶数,那么需要用最大流来判定。
最大流流的是什么?是反悔的边。
比如我们对应先前的无向边,我们任意选定了一个方向记录出度入度,那么另一个方向我们就需要连一条容量为1的边,代表我们有一次机会来返回。
然后对于出度比入度大的点我们可以将其连向汇点一条容量为出度入度之差一半的边。
入度比出度大的点我们可以从源点向他连一条容量为入度出度之差一半的边。
这样我们新建出来的图可以假装把它称为反悔图。
如果满流,显然我们可以保证这个欧拉回路存在,如果不满流那么说明这个欧拉回路不存在(无法流回,不知所措)。
代码:

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 1010
#define M 2010
#define LL L[i].l
#define RR L[i].r
#define C L[i].c1
#define CC L[i].c2
#define INF 0x3f3f3f3f
using namespace std;
int n,m;
int low,high;
struct line
{
    int l,r;
    int c1,c2;
    line(){}
    line(int _l,int _r,int _c1,int _c2):l(_l),r(_r),c1(_c1),c2(_c2){}
    friend istream& operator >> (istream &_,line &a)
    {scanf("%d%d%d%d",&a.l,&a.r,&a.c1,&a.c2);return _;}
    void update()
    {
        if(c1>c2)swap(c1,c2),swap(l,r);
        low=min(low,c1);
        high=max(high,c2);
    }
}L[M];
struct node
{
    int from,to,val;
    int next;
}edge[M<<2];
int head[N],cnt;
int in[N],out[N];
int dep[N],st,ed;
void init()
{
    low=0x3f3f3f3f,high=-1;
}
void init_before_check()
{
    memset(head,-1,sizeof(head));
    memset(in,0,sizeof(in));
    memset(out,0,sizeof(out));
    cnt=0,st=0,ed=n+1;
}
void edgeadd(int from,int to,int val)
{
    edge[cnt].from=from,edge[cnt].to=to;
    edge[cnt].val=val,edge[cnt].next=head[from];
    head[from]=cnt++;
}
bool bfs(int s,int e)
{
    memset(dep,0,sizeof(dep));
    queue<int>q;
    q.push(s);
    dep[s]=1;
    while(!q.empty())
    {
        int u=q.front();
        if(u==e)return 1;
        q.pop();
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int to=edge[i].to;
            if(dep[to]!=0||!edge[i].val)continue;
            dep[to]=dep[u]+1;
            q.push(to);
        }
    }
    return 0;
}
int dfs(int s,int max_vale)
{
    int ret=0;
    if(s==ed)return max_vale;
    for(int i=head[s];i!=-1;i=edge[i].next)
    {
        int to=edge[i].to;
        if(edge[i].val==0||dep[to]!=dep[s]+1)continue;
        int tmp=dfs(to,min(max_vale-ret,edge[i].val));
        edge[i].val-=tmp;
        edge[i^1].val+=tmp;
        ret+=tmp;
        if(ret==max_vale)return ret;
    }
    return ret;
}
bool if_has_odd()
{
    for(int i=1;i<=n;i++)
        if(abs(in[i]-out[i])&1)return 1;
    return 0;
}
bool check(int x)
{
    init_before_check();
    for(int i=1;i<=m;i++)
    {
        if(L[i].c2<=x)
            edgeadd(RR,LL,1),edgeadd(LL,RR,0);
        if(L[i].c1<=x)
            out[LL]++,in[RR]++;
    }
    if(if_has_odd())return 0;
    int judge=0;
    for(int i=1;i<=n;i++)
    {
        if(out[i]>in[i])
            edgeadd(i,ed,(out[i]-in[i])>>1),edgeadd(ed,i,0),judge+=(out[i]-in[i])>>1;
        else if(out[i]<in[i])
            edgeadd(st,i,(in[i]-out[i])>>1),edgeadd(i,st,0);
    }
    int ret=0;
    while(bfs(st,ed))
    {
        while(int t=dfs(st,INF))
        {
            ret+=t;
        }
    }
    return ret==judge;
}
int main()
{
    init();
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){cin>>L[i];L[i].update();}
    int l=low,r=high,ans=-1;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(check(mid))ans=mid,r=mid-1;
        else l=mid+1;
    }
    printf(ans==-1?"NIE\n":"%d\n",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值