混合图判断欧拉回路

(有任何问题欢迎留言或私聊

例题:POJ-1637-Sightseeing tour

题意:

翻译过来就是:
 1. 有n个点,m条边;边用u,v,w描述,w==1表示有向边,w==0表示无向边。
 2. 判断途中是否存在欧拉回路(一条路径不重复地经过所有的边一次)。

思路:

 1. 不论无向边的方向怎么指定,若存在欧拉回路,每个点出度和入度的差一定为偶数。因为有向图欧拉回路每个点的出度等于入度,反向一条边后,度数差变换2。

 2. 因为不知道怎么指定无向边的方向,这时可以利用到最大流的调整流量的特性。给每条无向边随意定一个方向,建边u->v,流量为1。这条边可能被最大流过程调整。

 3. 建图过程:这里的度数差 = 出度-入度
  遍历每个点,若度数差为奇数,则不存在欧拉回路,break;
  若度数为正数,则源点向节点连边,流量为度数差/2;
  若度数为负数,则节点向汇点连边,流量为度数差的绝对值/2。

 4. 为什么流量要等于度数差的一半呢?
  想想,这个图已经被我们变成有向图了是吧。你知道有向图的欧拉回路每个点的入度等于出度。举个栗子,有一个点的出度比入度多4,这时是不是只需要且只能改变两条边的方向,入度才能等于出度?显然是这样的。所以源点和这个节点连边,流量设为度数差/2,表示你可以通过网络流调整某些边(数量为度数差/2)的方向让它出度等于入度,而且必须调整。

 5.最大流跑完了又要怎么判断是否存在欧拉回路呢?
 答案是,遍历每条源点连出的边,判断是否满流。必须所有的边都满流才存在欧拉回路。因为只要有一条边不是满流,就表示那个节点的入度不等于出度,这点前面讲过了。

 6. 至此流程就跑完了,建议画个图辅助一下理解,不管画的好不好看,自己看得懂就行。
这里写图片描述

相关类型题要注意的要点:

 1. 连通性;(依题而定,方法有:dfs,并查集等)
 2. 出度与入度的关系,奇数度的数量;(分有向图,无向图,混合图)
 3.dfs的起点选择让我很难受

AC代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int MX=205;
int s,t,e,fr[MX],h[MX],nh[MX],du[MX];
LL ans;
int n,m;
const LL inf=1e17;
struct lp
{
    int t,ne;
    LL w;
}a[MX*2];
inline int ab(int x){
    return x<0?0-x:x;
}
void ins(int f,int t,LL w)
{
    a[++e].t=t,a[e].ne=fr[f],fr[f]=e,a[e].w=w;
    a[++e].t=f,a[e].ne=fr[t],fr[t]=e,a[e].w=0;
}
LL sap(int u,LL fl)
{
    if (u==t) return fl;
    LL res=fl;
    for (int i=fr[u];i;i=a[i].ne){
        if (a[i].w&&h[a[i].t]+1==h[u]){
            LL t=sap(a[i].t,min(res,a[i].w));
            a[i].w-=t,a[i^1].w+=t;
            if (!(res-=t)) return fl;
        }
    }
    if (!(--nh[h[u]])) h[t]=t;
    ++nh[++h[u]];
    return fl-res;
}
void init(){
    memset(fr,0,sizeof(fr));
    memset(nh,0,sizeof(nh));
    memset(h,0,sizeof(h));
    memset(du,0,sizeof(du));
    s=1,t=n+2;ans=0;e=1;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        init();
        for (int i=1,x,y,z;i<=m;i++){
            scanf("%d%d%d",&x,&y,&z);
            if(z==0) ins(x+1,y+1,1);
            du[x]++;du[y]--;
        }
        int flag=0;
        for(int i=1;i<=n;++i){
            if(ab(du[i])%2!=0){
                flag=1;
                break;
            }
            if(du[i]>0){
                ins(s,i+1,du[i]/2);
            }else if(du[i]<0){
                ins(i+1,t,-du[i]/2);
            }
        }
        if(flag){
            printf("impossible\n");
            continue;
        }
        nh[0]=t;
        while (h[t]!=t){ 
            ans+=sap(s,inf);
        }
        for(int i=fr[s];i;i=a[i].ne){
            if(i%2==0&&a[i].w!=0){
                flag=1;
                break;
            }
        }
        if(flag)printf("impossible\n");
        else printf("possible\n");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39599067/article/details/80189866