[POJ1637] Sightseeing tour

POJ1637 观光旅行

题目链接 : http://poj.org/problem?id=1637

Description:
给定一个 n 个节点, m 条边的混合图,有一些边是有向边,有一些边是无向边。
请给无向边定向,使得最后的有向图存在经过所有边仅一次的回路。并给出方案。
其中 1 ≤ n ≤ 200, 1 ≤ m ≤ 2000 。

模型:混合图欧拉回路问题 - 网络流

1.定义 : 混合欧拉回路问题,即在一张包含有向边和无向边的混合图中求欧拉回路

2.有向图欧拉回路判断:所有顶点的入度与出度之差为0。

3.混合图欧拉回路 :
首先,我们把无向边设定为任意一个方向
虽然不一定是欧拉回路
但是我们可以进行反悔操作

如果进行反悔操作
对于跟这条边连的点
入度++,出度--
或者
入度--,出度++

所以改变一条边的方向后
一个点入度/出度之差会+2或-2

所以在我们任意连边后,如果一个的(入度-出度)为奇数
那么它无论如何也不能变成出入度之差为0的点
于是impossible

然后接下来就要用到网络流了
我们不妨设一个点的入度为--\(in[v]\)出度为--\(ou[v]\)
对于一个点v
有一个非常重要的量\(tmp_v=ou[v]-in[v]\)

我们先用原始人的思维想一下
我们如果改变一条边 u -> v 的方向
那么\(tmp_u-=2 \&\& tmp_v+=2\)
所以我们可以超级暴力地枚举更换哪一条边的方向
就这么加加减减
直到所有的$ in[i]=ou[i]=0 $

然后可以考虑用网络流优化
引用自 https://blog.csdn.net/chenzhenyu123456/article/details/48302459

我们可以构建网络流模型,判断剩余度数能否完全分配。下面需要确定的是模型中边的方向.

假设我们定向原图中边<u, v>方向为u -> v。
1,当u的出度大于入度,才可能把多的出度分配给v,当做v的入度。这个度数能否成功分配可以用能否到达汇点sink来判定。

2,当u的出度小于入度,v是不可能把多的出度分配给u的,因为v->u没有边。这个时候我们必须要从source给点u分配度数。


这样建边方向就出来了,若确定无向边<u, v>方向为u -> v。

那么当点的in < out时,源点source向它建边,容量为差值的一半。当点的out > in时,它向汇点建边,容量为差值的一半。

建图:设置超级源点source,超级汇点sink。

1,对于边<u, v>,若它是无向的,我们人为定向,u向v建边,容量为1。当欧拉路径中是v -> u的,u多出的度数可以流到v。

2,若in[i] < out[i],则source向i建边,容量为正差值的一半;

3,若in[i] > out[i],则i向sink建边,容量为正差值的一半。

统计从source流入的流量sum,最后判断是否满流即可。
/*
一个并不能AC
但是把重要思想体现出来的代码
原谅我实在不想做POJ的题目QvQ
*/
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <queue>

#define rg register int
#define ll long long
#define RG register 
#define il inline

using namespace std;

il int gi() {
    rg x=0,o=0;RG char ch=getchar();
    while(ch!='-'&&(ch<'0'||'9'<ch)) ch=getchar();
    if(ch=='-') o=1,ch=getchar();
    while('0'<=ch&&ch<='9') x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return o?-x:x;
}

#define N 501
#define M 20001
const int inf=214748364;

int s,t,n,m,Ans,sum;
int in[N],ou[N];

struct Edge { int to,nxt,w; } e[M];
int Ehead[N],Ecnt=2;
il void Eadd(int u,int v,int w) {
    e[Ecnt]=(Edge){v,Ehead[u],w};
    Ehead[u]=Ecnt++;
    e[Ecnt]=(Edge){u,Ehead[v],0};
    Ehead[v]=Ecnt++;
}

int lev[N],cur[M];
queue <int> Q;
il bool bfs() {
    while(!Q.empty()) Q.pop();
    memset(lev,-1,sizeof(lev));
    Q.push(s),lev[s]=0;
    while(!Q.empty()) {
        int u=Q.front(); 
        Q.pop();
        for(int v,i=Ehead[u]; i; i=e[i].nxt) {
            v=e[i].to;
            if(e[i].w<=0 || lev[v]>0) continue;
            lev[v]=lev[u]+1;
            Q.push(v);
        }
    }
    return lev[t]>0;
}
int dfs(int u,int f) {
    if(u==t || !f) return f;
    for(int &i=cur[u]; i; i=e[i].nxt) {
        int v=e[i].to,di=0;
        if(lev[v]!=lev[u]+1) continue;
        if(di=dfs(v,min(f,e[i].w))) {
            e[i].w-=di;
            e[i^1].w+=di;
            return di;
        }
    }
    return 0;
}
il void maxflow() {
    int di;
    while(bfs()) {
        for(int i=s; i<=t; ++i) cur[i]=Ehead[i];
        Ans+=dfs(s,inf);
    }
}

int main() {
    n=gi(),m=gi();
    s=0,t=n+1;
    for(int u,v,w,i=1;i<=m;++i) {
        u=gi(),v=gi(),w=gi();
        ++in[v],++ou[u];
        if(!w) Eadd(u,v,1);
    }
    for(int tmp,i=1; i<=n; ++i) {
        tmp=abs(in[i]-ou[i]);
        if(tmp&1) {
            puts("Impossible");
            return 0;
        }
        tmp>>=1;
        if(in[i]<ou[i]) sum+=tmp, Eadd(s, i, tmp);
        else Eadd(i, t, tmp);
    }
    maxflow();
    Ans==sum?puts("possible"):puts("impossible");
    return 0;
}

转载于:https://www.cnblogs.com/tply/p/8808721.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值