欧拉图、哈密尔顿图

p.s. 二者的必要条件是图为连通图

欧拉图

定义

欧拉通路:能一次性走完一张图上所有的边,且每条边只经过一次
欧拉回路:能一次性走完一张图上所有的边,每条边只经过一次,且这条路径构成一个回路(即最终回到了出发点)

有欧拉回路的图成为欧拉图(Eulerian),有欧拉通路但无回路的称为半欧拉图(semi-Eulerian)

欧拉回路必须满足的条件:

无向图:度数为奇数的点的个数 == 0 或者 == 2(== 0 : 欧拉回路, == 2 :欧拉通路)

有向图:除了起点和终点,每个点的入度 == 出度,满足 起点的出度-入度 == 1 && 终点入度-出度 == 1 或者 起点终点为同一个

思路

dfs

首先需要确定dfs的起点,这可以通过输入边时统计度数实现,如果奇数度数的点不是0或2,则无欧拉通路;有两个奇数点的,找到其中一个点作为起点;度数全为偶数的,任意指定一个点作为起点。

然后开始dfs,每经过一条路时就把它的次数--,免得重复经过,当无路可走时,就存下当前的点(注意是逆序),回溯,继续遍历

模版题1

https://www.luogu.com.cn/problem/P2731

这题不仅要先判断,还要输出字典序最小的遍历序列,这里有坑。

关于为什么不能正序输出但可以倒序:

首先,在无向图中,如果有一条欧拉路,起点为 \(v\) ,用一个点 \(u\) 向已知起点 \(v\) 再连一条边,那么现在就有了从 \(u\) 出发的一条欧拉路。

倒序输出相当于,之前先找到了以 \(v\) 为起点的一条欧拉路,即先解决子问题,现在在它前面加入\(u\)

正序输出相当于,给你一个大问题,你没有解决子问题,但是先规定先从当前点出发,不能保证从当前出发能找到欧拉路的正确性

code

#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
using namespace std;
const int N = 500 + 10;
int m, n = 500, u, v;
int rd[N], path[N][N];
int start = 1;
int cnt, ans[N];

void dfs(int u) {
    for(int i = 1; i <= 500; i++) {
        if(path[u][i]) {
            path[u][i]--;
            path[i][u]--;  //  有向图删掉
            dfs(i);
        }
    }
    ans[++cnt] = u;
}

int main() {  
    ios::sync_with_stdio(false);
    cin >> m;
    for(int i = 1; i <= m; i++) {
        cin >> u >> v;
        path[u][v]++; path[v][u]++;
        rd[u]++; rd[v]++;
    }
    for(int i = 1; i <= 500; i++) {
        if(rd[i] % 2) {
            start = i;
            break;
        }
    }
    dfs(start);
    for(int i = cnt; i >= 1; i--) cout << ans[i] << endl;
    return 0;
}

模版题2

https://www.luogu.com.cn/problem/P1341

感觉建图挺有意思的,可以自己先思考。

一对点对相当于给这两个字母连了一条无向边。

#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
using namespace std;
const int N = 500 + 10;
int m, n = 500;
string s;
int rd[N], path[N][N];
int start;
int cnt, ans[N];

inline int chg(char ch) {
    return (int)(ch);
}

void dfs(int u) {
    for(int i = 1; i <= 200; i++) {
        if(i >= 'a' && i <= 'z' || i >= 'A' && i <= 'Z') {
            if(path[u][i]) {
                path[u][i]--;
                path[i][u]--;  //  有向图删掉
                dfs(i);
            }
        }
    }
    ans[++cnt] = u;
}

int main() {  
    ios::sync_with_stdio(false);
    cin >> m;
    for(int i = 1, u, v; i <= m; i++) {
        cin >> s;
        u = chg(s[0]); v = chg(s[1]);
        path[u][v]++; path[v][u]++;
        rd[u]++; rd[v]++;
    }
    int odd = 0;
    for(int i = 1; i <= 200; i++) {
        if(i >= 'a' && i <= 'z' || i >= 'A' && i <= 'Z') {
            if(rd[i] % 2) {
                odd++;
            }
        }
    }
    if(odd != 0 && odd != 2) {
        cout << "No Solution\n";
        return 0;
    }
    if(odd == 0) {
        for(int i = 1; i <= 200; i++) {
            if(rd[i]) {
                start = i;
                break;
            }
        }
    }
    else {
        for(int i = 1; i <= 200; i++) {
            if(rd[i] % 2) {
                start = i;
                break;
            }
        }
    }
    dfs(start);
    // cout << "cnt = " << cnt << endl; /// 
    for(int i = cnt; i >= 1; i--) cout << (char)ans[i];
    cout << '\n';
    return 0;
}

哈密尔顿图

定义

哈密尔顿路:经过一张图上所有的点的通路
哈密尔顿回路:经过一张图上所有的点的通回路

有哈密尔顿回路的图成为哈密尔顿图(Hamilton),有哈密尔顿通路但无回路的称为半哈密尔顿图(semi-Hamilton)

posted @ 2023-07-02 20:42  starlightlmy  阅读(103)  评论(0编辑  收藏  举报