BZOJ 2095 [Poi2010]Bridges 二分 最大流(混合图欧拉回路)

来源:互联网 发布:战争电影推荐 知乎 编辑:程序博客网 时间:2024/05/29 04:07

题目大意:给出一张n个点m条边的联通图,无重边,每条边有正反两个权值。现要从点1出发经过每条边每个点一次,问最大边权最小是多少。

最大最小容易想到二分,问题是如何判断是否有欧拉回路。

图不连通自然没有欧拉回路。
将大于限制的边设为不可走,新的图变成了混合图,即同时存在有向边和无向边。
首先,有向图的欧拉回路存在的条件是入度等于出度。
可以将无向边随意定向,默认从x到y。记一个点的度数为入度-出度。现在问题转化为如何转换无向边的方向使所有点的度数为0。若度数为奇数则一定没有欧拉回路因为将一条边反向会使一个点的度数加或减2,一定不可能得到0。

用网络流来判断。
对于每条无向边,y向x连边,流量为1,表示边可以反向。
对于每个点x,如果度数大于0,源点向x连边,否则x向汇点连边,流量为度数除以2,表示一个点的度数变成0需要的变化量。
如果满流则说明有欧拉回路

#include <cstdio>#include <cstring>#include <algorithm>#include <queue>#define N 1005#define INF 1000000000using namespace std;struct Data {    int a,b,c,d;    void scan() {        scanf("%d%d%d%d",&a,&b,&c,&d);        if(c>d) swap(c,d), swap(a,b);    }}g[N*2];struct Edge {    int from,to,nxt,cap;    Edge() {}    Edge(int _from,int _to,int _nxt,int _cap):        from(_from),to(_to),nxt(_nxt),cap(_cap) {}}e[N*10];int n,m,tot=-1,S,T,fir[N],d[N],pa[N],deg[N];int root(int x) { return pa[x]==x ? pa[x] : pa[x]=root(pa[x]); }void Add_Edge(int from,int to,int cap) {    e[++tot]=Edge(from,to,fir[from],cap), fir[from]=tot;    e[++tot]=Edge(to,from,fir[to],0), fir[to]=tot;    return ;}bool bfs() {    queue<int> q;    for(int i=S;i<=T;++i) d[i]=-1;    d[S]=0, q.push(S);    while(!q.empty()) {        int x=q.front(); q.pop();        for(int i=fir[x];~i;i=e[i].nxt) {            if(!e[i].cap || d[e[i].to]!=-1) continue;            d[e[i].to]=d[x]+1;            if(e[i].to==T) return true;            q.push(e[i].to);        }    }    return false;}int dfs(int x,int now) {    if(!now || x==T) return now;    int f,flow=0;    for(int i=fir[x];~i;i=e[i].nxt) {        if(d[e[i].to]!=d[x]+1) continue;        f=dfs(e[i].to,min(e[i].cap,now));        if(!f) continue;        e[i].cap-=f, e[i^1].cap+=f;        now-=f, flow+=f;        if(!now) break;    }    if(!flow) d[x]=-1;    return flow;}int Dinic() {    int maxflow=0;    while(bfs()) maxflow+=dfs(S,INF);    return maxflow;}bool check(int lim) {    fir[S]=fir[T]=-1;    tot=-1;    for(int i=1;i<=n;++i) pa[i]=i, deg[i]=0, fir[i]=-1;    int cnt=n;    for(int i=1;i<=m;++i) {        if(g[i].d<=lim) {            ++deg[g[i].a], --deg[g[i].b];            int pa_a=root(g[i].a),pa_b=root(g[i].b);            if(pa_a!=pa_b) pa[pa_a]=pa_b, --cnt;            Add_Edge(g[i].a,g[i].b,1);        }        else if(g[i].c<=lim) {            --deg[g[i].a], ++deg[g[i].b];            int pa_a=root(g[i].a),pa_b=root(g[i].b);            if(pa_a!=pa_b) pa[pa_a]=pa_b, --cnt;        }    }    if(cnt>1) return false;    for(int i=1;i<=n;++i) {        if(deg[i]&1) return false;        if(deg[i]>0) Add_Edge(S,i,deg[i]>>1);        else Add_Edge(i,T,-deg[i]>>1);    }    Dinic();    for(int i=fir[S];~i;i=e[i].nxt)        if(e[i].cap) return false;    return true;}int main() {    scanf("%d%d",&n,&m);    T=n+1;    for(int i=1;i<=m;++i) g[i].scan();    int l=1,r=1001,mid,ans=-1;    while(l<=r) {        mid=l+r>>1;        if(check(mid)) ans=mid, r=mid-1;        else l=mid+1;    }    if(ans==-1) puts("NIE");    else printf("%d\n",ans);    return 0;}
阅读全文
0 0
原创粉丝点击