图论学习之欧拉路

欧拉回路:图G,若存在一条路,经过G中每条边有且仅有一次,称这条路为欧拉路,如果存在一条回路经过G每条边有且仅有一次,

称这条回路为欧拉回路。具有欧拉回路的图成为欧拉图。

判断欧拉是否存在的方法

有向图:图连通,有一个顶点出度大入度1,有一个顶点入度大出度1,其余都是出度=入度。

无向图:图连通,只有两个顶点是奇数度,其余都是偶数度的。

判断欧拉回路是否存在的方法

有向图:图连通,所有的顶点出度=入度。

无向图:图连通,所有顶点都是偶数度。

程序实现一般是如下过程:

1.利用并查集判断图是否连通,pre[i]==i的个数,如果大于1,说明不连通。

2.根据出度入度个数,判断是否满足要求。

3.利用dfs输出路径(先搜子节点,然后当前节点保存起来,最后倒着输出)。

先看个启蒙题

hdu 1878

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")

using namespace std;
#define   MAX           10005
#define   MAXN          200005
#define   maxnode       1005
#define   sigma_size    4
#define   lson          l,m,rt<<1
#define   rson          m+1,r,rt<<1|1
#define   lrt           rt<<1
#define   rrt           rt<<1|1
#define   mid           int m=(r+l)>>1
#define   LL            long long
#define   ull           unsigned long long
#define   mem(x,v)      memset(x,v,sizeof(x))
#define   lowbit(x)     (x&-x)
#define   pii           pair<int,int>


const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const int    INFF  = 1e9;
const double pi    = 3.141592653589793;
const double inf   = 1e18;
const double eps   = 1e-10;
const LL     mod   = (1<<64);

/**************¶áèëía1ò*********************/
inline int read_int(){
    int ret=0;
    char tmp;
    while(!isdigit(tmp=getchar()));
    do{
        ret=(ret<<3)+(ret<<1)+tmp-'0';
    }
    while(isdigit(tmp=getchar()));
    return ret;
}
/*******************************************/

int in[MAX];
int pre[MAX];
int n,m;

void init(){
    mem(in,0);
    for(int i=1;i<=n;i++) pre[i]=i;
}

int Find(int x){
    return pre[x]==x?x:pre[x]=Find(pre[x]);
}

void Union(int a,int b){
    pre[Find(a)]=pre[Find(b)];
}

int main(){
    while(scanf("%d",&n)&&n){
        scanf("%d",&m);
        init();
        for(int i=0;i<m;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            Union(a,b);
            in[a]++;
            in[b]++;
        }
        int ans=0;
        int flag=0;
        for(int i=1;i<=n;i++){
            if(pre[i]==i) ans++;
            if(in[i]%2) flag=1;
        }
        if(ans>1||flag) printf("0\n");
        else printf("1\n");
    }
    return 0;
}


再看一个难一点的

poj 2337

题意:给你n个单词,让你收尾想接,把他们连成一个串,输出字典序最小的那个串

题解:首先这题的建图不能用常规方法,应该一条有向边代表一个单词,首字母和尾字母当成节点,建图

因为字典序要求最小,所以肯定是从尽可能小的字母(节点)开始dfs

首先输入单词,然后排序,从字典序大的开始建图(这样用前向星遍历图的时候,保证先遍历的边是字典序比较小的)

然后判定能否组成欧拉路(入度,出度判断之)

这样还不够,还要判断图是否连通

如果是欧拉路,那就从出度等于入度+1的那个点开始搜,如果是回路,那就从最小的节点开始搜

dfs的时候看这个节点搜过没有,如果没有的话就搜索他,然后把这条边放入ans数组中(输出顺序应该倒过来,因为你最后搜到终点的时候,先把终点前一条边放入,然后再往前放)

可以画图考虑一下,理解之后就知道欧拉路如何输出路径了(判定是很简单的,主要就路径一开始有点不知道如何输出,没把握)

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")

using namespace std;
#define   MAX           1005
#define   MAXN          200005
#define   maxnode       1005
#define   sigma_size    4
#define   lson          l,m,rt<<1
#define   rson          m+1,r,rt<<1|1
#define   lrt           rt<<1
#define   rrt           rt<<1|1
#define   mid           int m=(r+l)>>1
#define   LL            long long
#define   ull           unsigned long long
#define   mem(x,v)      memset(x,v,sizeof(x))
#define   lowbit(x)     (x&-x)
#define   pii           pair<int,int>


const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const int    INFF  = 1e9;
const double pi    = 3.141592653589793;
const double inf   = 1e18;
const double eps   = 1e-10;
const LL     mod   = (1<<64);

/**************¶áèëía1ò*********************/
inline int read_int(){
    int ret=0;
    char tmp;
    while(!isdigit(tmp=getchar()));
    do{
        ret=(ret<<3)+(ret<<1)+tmp-'0';
    }
    while(isdigit(tmp=getchar()));
    return ret;
}
/*******************************************/

struct Edge{
    int v,next;
    int index;
    bool flag;
}edge[MAX*MAX];
int head[MAX];
int in[MAX];
int out[MAX];
int tot;
int n,m;
int ans[MAX];
int cnt;
string s[1005];

void add_edge(int a,int b,int i){
    edge[tot]=(Edge){b,head[a],i,false};
    head[a]=tot++;
}
void init(){
    mem(in,0);
    mem(out,0);
    mem(head,-1);
    mem(ans,0);
    cnt=0;
    tot=0;
}

bool dfs(int u){
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        if(!edge[i].flag){
            edge[i].flag=true;
            dfs(v);
            ans[cnt++]=edge[i].index;
        }
    }
}

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        init();
        for(int i=0;i<n;i++) cin>>s[i];
        sort(s,s+n);
        int start=100;
        for(int i=n-1;i>=0;i--){
            int a=s[i][0]-'a';
            int b=s[i][s[i].size()-1]-'a';
            add_edge(a,b,i);
            out[a]++;
            in[b]++;
            start=min(min(a,b),start);
        }
        int as1=0;
        int as2=0;
        for(int i=0;i<26;i++){
            if(in[i]==out[i]+1){
                as1++;
            }
            else if(in[i]+1==out[i]){
                as2++;
                start=i;
            }
            else if(in[i]!=out[i]) as1=3;//如果出现上面两种情况之外的不等于,那就直接说明不成立
        }
        if(!((as1==0&&as2==0)||(as1==1&&as2==1))) printf("***\n");
        else{
            dfs(start);
            if(cnt!=n) printf("***\n");//判断是否连通
            else{
                for(int i=cnt-1;i>=0;i--){
                    cout<<s[ans[i]];
                    if(i) printf(".");
                    else printf("\n");
                }
            }
        }
    }
    return 0;
}




你可能感兴趣的:(ACM)