【混合图欧拉回路】POJ 1637

混合图的欧拉回路问题。

    混合图就是边集中有有向边和无向边同时存在。这时候需要用网络流建模求解。

    建模:

    把该图的无向边随便定向,计算每个点的入度和出度。如果有某个点出入度之差为奇数,那么肯定不存在欧拉回路。 因为欧拉回路要求每点入度 = 出度,也就是总度数为偶数,存在奇数度点必不能有欧拉回路。

    好了,现在每个点入度和出度之差均为偶数。那么将这个偶数除以2,得x。也就是说,对于每一个点,只要将x条边改变方向(入>出就是变入,出>入就是变出),就能保证出 = 入。如果每个点都是出 = 入,那么很明显,该图就存在欧拉回路。

    现在的问题就变成了:我该改变哪些边,可以让每个点出 = 入?构造网络流模型。

    首先,有向边是不能改变方向的,要之无用,删。一开始不是把无向边定向了吗?定的是什么向,就把网络构建成什么样,边长容量上限1。另新建s和t。对于入 > 出的点u,连接边(u, t)、容量为x,对于出 > 入的点v,连接边(s, v),容量为x(注意对不同的点x不同)。
    之后,察看从S发出的所有边是否满流。有就是能有欧拉回路,没有就是没有。欧拉回路是哪个?察看流值分配,将所有流量非 0(上限是1,流值不是0就是1)的边反向,就能得到每点入度 = 出度的欧拉图。 
    由于是满流,所以每个入 > 出的点,都有x条边进来,将这些进来的边反向,OK,入 = 出了。对于出 > 入的点亦然。那么,没和s、t连接的点怎么办?和s连接的条件是出 > 入,和t连接的条件是入 > 出,那么这个既没和s也没和t连接的点,自然早在开始就已经满足入 = 出了。那么在网络流过程中,这些点属于“中间点”。我们知道中间点流量不允许有累积的,这样,进去多少就出来多少,反向之后,自然仍保持平衡。 
    所以,就这样,混合图欧拉回路问题,解了。

#define MIN INT_MIN
#define MAX INT_MAX
#define N 220
int min(int a,int b){return a>b?b:a;}
int g[N][N];
int du[N];
int res[N];
int pre[N];
int f[N][N];
int n;
bool EK(int s,int t,int cnt){
    int i,j;
    queue<int> qq;
    memset(f,0,sizeof(f));
    int ans=0;
    while(1){
        memset(res,0,sizeof(res));
        res[s] = MAX;
        pre[s] = -1;
        qq.push(s);
        while(!qq.empty()){
            int u = qq.front();
            qq.pop();
            for(int v=s;v<=t;v++){
                if(!res[v] && f[u][v] < g[u][v]){
                    qq.push(v);
                    pre[v] = u;
                    res[v] = min(res[u] , g[u][v] - f[u][v]);
                }
            }
        }
        if(!res[t])break;
        int k = t;
        while(pre[k] != -1){
            f[pre[k]][k] += res[t];
            f[k][pre[k]] -= res[t];
            k = pre[k];
        }
    }
    for(i=1;i<=n;i++){
        ans+=f[0][i];
    }
    return ans==cnt;
}
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        int m;
        scanf("%d%d",&n,&m);
        int i,j;
        memset(du,0,sizeof(du));
        memset(g,0,sizeof(g));
        while(m--){
            int a,b,type;
            scanf("%d%d%d",&a,&b,&type);
            du[a]++;
            du[b]--;
            if(!type){
                g[a][b]++;
            }
        }
        bool ok = 1;
        for(i=1;i<=n;i++){
            if(abs(du[i]) % 2 == 1){//若点的出度与入度差为奇数就一定不存在欧拉回路
                ok = 0;
                break;
            } else {
                du[i] = du[i]/2;//网络中每条边的权值都为出度与入度差的一半
            }
        }
        if(!ok){
            puts("impossible");
            continue;
        }
        int cnt=0;
        for(i=1;i<=n;i++){
            if(du[i]<0){//出<入,连至汇点
                g[i][n+1] = abs(du[i]);
            } else if(du[i]>0){//出>入,连至源点
                g[0][i] = du[i];
                cnt+=du[i];//cnt是验证是否满流
            }
        }
        if(EK(0,n+1,cnt)){
            puts("possible");
        } else puts("impossible");
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值