欧拉路径&&欧拉回路

欧拉回路

无向图:图连通且每个点的度均为偶数

有向图:图连通且每个点入度等于出度

欧拉路径

无向图:图连通且只有两个奇点

有向图:图连通且一个点出度-入度为1,一个点入度-出度为1,其他点均入度等于出度

混合图的欧拉回路

1、随意定向

在混合图中,对于双向边的处理除了拆边之外,还有任意定向。先对全图的双向边进行任意定向,计算出度和入度,如果一个点的出度加入度是奇数的话,那么这个图中没有欧拉回路。虽然随意定向是没有依据的,但是可以使用这样的随机化处理方法,再使用恰当的调整方法构造出解。

2、自调整方法

将其中的一些边的方向调整回来,使所有的点的出度等于入度。但是有一条边的方向改变后,可能会改变一个点的出度的同时改变另一个点的入度,相当于一条边制约着两个点。同时有些点的出度大于入度,迫切希望它的某些点出边转向;而有些点的入度大于出度,迫切希望它的某些入边转向。这两条边虽然需求不同,但是他们之间往往一条边转向就能同时满足二者。

具体步骤:

1、另 x = |入度-出度| / 2,对于不同的点有不同的x值,这个x值代表它们在邻接表中相应调整x条就能让出度等于入度

2、把图中的点转换为一个二分图,每个点的x值就是它们的点权。

3、源点S向所有出度>入度的点连边,汇点T向所有入度大于出度的点连边,将各自的点权转换为边权。

4、最后将原图中所有暂时定向的无向边加上一个1的容量,方向不变,而有向边不能改变方向,不需连边

可以发现,从源点S出发的一个单位流将会把一个“无向边”的容量变为0,使得两端的点权各自减1,其实这就是在模拟一次对无向边方向的调整。当把图建好后,依靠最大流性质可以最大可能地无冲突调整边的方向,并最终使得每个点的点容量都达到满流。对那些图中出度等于入度的点做适当分析,它们作为一个“中间点”,由于流平衡性质,不会留下任何流量值,对于那些真正需要调整的点不会带来任何影响。最后检查从源点出发的每条边是否都满流,如果有一条边没有满流,说明有一个点没有调整到入度等于出度,于是整个图不存在欧拉回路。

例题

从一个点出发逛遍所有的路回到出发点,路分为单行路和双行路,这就是一个混合图求欧拉回路的问题。

#include <math.h>
#include <vector>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>

using namespace std;

const int MAXV = 1010;
const int MAXE = 50010;
const int INF = 0x3f3f3f3f;

int n,m;
int S,T;
int totflow;//满流大小
int dis[MAXV],vis[MAXV],Q[MAXV],h,t;
int out[MAXV],in[MAXV],head[MAXV],cnt;

struct edge{
    int u,v,c,next;
}node[MAXE];

void Init(){
    cnt = 0, totflow = 0;
    memset(in, 0, sizeof(in));
    memset(out, 0, sizeof(out));
    memset(head, -1, sizeof(head));
}

void addedge(int u, int v, int c){//构建邻接表
    node[cnt].v = v;
    node[cnt].c = c;
    node[cnt].next = head[u];
    head[u] = cnt++;
}

int buildflow(){//创建最大流模型
    int flag = true;
    S = 0, T = n+1;
    for(int i=1; i<=n; i++){
        if((out[i] + in[i]) & 1) return 0;
        else if(out[i] > in[i]){
            int c = (out[i] - in[i]) >> 1;
            addedge(S, i, c);
            addedge(i, S, 0);
            totflow += c;
        } 
        else{
            int c = in[i] - out[i];
            addedge(i, T, c/2);
            addedge(T, i, 0);
        }
    }
    return 1;
}

int BFS(){//构建层次图
    memset(dis, -1, sizeof(dis));
    Q[1] = S, h = 0, t = 1;
    dis[S] = 0;
    while(h < t){
        int u = Q[++h];
        for(int i=head[u]; i!=-1; i=node[i].next){
            int v = node[i].v, c = node[i].c;
            if(dis[v] == -1 && c){
                dis[v] = dis[u] + 1;
                Q[++t] = v;
                if(v == T) return true;
            }
        }
    }
    return false;
}

int DFS(int id, int minflow){//Dinic算法过程
    if(id == T) return minflow;
    int a = 0;
    for(int i=head[id]; i!=-1; i=node[i].next){
        int v = node[i].v, c = node[i].c;
        if(c > 0 && dis[v] == dis[id] + 1 
            && (a = DFS(v, min(minflow, c)))){
            node[i].c -= a;
            node[i^1].c += a;
            return a;
        }
    }
    return 0;
}

int Dinic(){
    int tmp,ans = 0;
    while(BFS()){
        while(tmp = DFS(S, INF)) ans += tmp;
    }
    return ans;
}
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        Init();
        scanf("%d%d",&n,&m);
        while(m--){
            int u,v,d;
            scanf("%d%d%d",&u,&v,&d);
            out[u]++, in[v]++;
            if(u == v) continue;
            if(d) continue;
            addedge(u, v, 1);
            addedge(v, u, 0);
        }
        int flag = buildflow(),  ans = Dinic();
        if(!flag) puts("impossible");
        else if(ans >= totflow) puts("possible");  
        else puts("impossible");   
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值