当前位置:首页 > C++目录 > 正文内容

一笔画问题

亿万年的星光3个月前 (11-30)C++目录391


【题目描述】

如果一个图存在一笔画,则一笔画的路径叫做欧拉路,如果最后又回到起点,那这个路径叫做欧拉回路。

根据一笔画的两个定理,如果寻找欧拉回路,对任意一个点执行深度优先遍历;找欧拉路,则对一个奇点执行dfs,时间复杂度为O(m+n),m为边数,n是点数。

【输入】

第一行n,m,有n个点,m条边,以下m行描述每条边连接的两点。

【输出】

欧拉路或欧拉回路,输出一条路径即可。

【输入样例】

5 5
1 2
2 3
3 4
4 5
5 1

【输出样例】

1 5 4 3 2 1

【提示】

【数据范围】

对于100%的数据:1 < n < 100,1 < m < 2000。



【知识补充】

欧拉路与欧拉回路判定条件

对于无向图:

  1. 欧拉回路(回到起点):

    • 所有顶点的度数均为偶数。

    • 图是连通的(边集非空且能通过边到达所有边相关的顶点)。

  2. 欧拉路(不要求回到起点):

    • 恰好有两个顶点的度数为奇数(起点和终点),其余均为偶数。

    • 图是连通的。


【思路】

  1. 读入图,统计每个点的度数。

  2. 检查奇数度顶点的个数:

    • 0 个:欧拉回路,任选一个(有边的)起点。

    • 2 个:欧拉路,选一个奇数度点作为起点。

    • 其他:无欧拉路/回路(但题目保证存在)。

  3. 找欧拉路径:

    • 从起点 DFS,每次选择一条未访问的边,递归后把点加入路径。

    • 最后逆序输出路径。



【参考答案】

#include <iostream>
#include <vector>
#include <stack>
#include <algorithm>
using namespace std;
const int MAXN = 105;  // 最大点数,
const int MAXM = 2005; // 最大边数,
int n, m;              // 实际点数和边数
int deg[MAXN];         // 存储每个顶点的度数
int G[MAXN][MAXN];     // 邻接矩阵存储图,G[u][v] 表示 u 到 v 的边数(处理重边)
vector<int> path;      // 存储最终的欧拉路径

 //深搜找欧拉路径
void dfs(int u) {
    // 遍历所有可能的邻接点
    for (int v = 1; v <= n; v++) {
        // 如果 u 到 v 还有未访问的边
        if (G[u][v] > 0) {
            // 删除这条边(无向图要删除两个方向)
            G[u][v]--;
            G[v][u]--;
            // 递归访问下一个顶点
            dfs(v);
            // 注意:这里没有立即将顶点加入路径,
            // 而是在递归返回后才加入,这是关键
        }
    }
    // 当 当前顶点的所有边都处理完后,将顶点加入路径
    // 这样保证路径是逆序构建的
    path.push_back(u);
}

int main() {
    cin >> n >> m; // 读入点数和边数
    // 读入每条边并构建图
    for (int i = 0; i < m; i++) {
        int u, v;
        cin >> u >> v;
        // 在邻接矩阵中记录边(无向图)
        G[u][v]++;
        G[v][u]++;
        // 更新顶点的度数
        deg[u]++;
        deg[v]++;
    }
    // 寻找欧拉路径的起点
    int start = 1;  // 默认从顶点1开始(用于欧拉回路情况)
    // 检查是否存在奇数度顶点(欧拉路情况)
    for (int i = 1; i <= n; i++) {
        if (deg[i] % 2 == 1) {
            // 找到第一个奇数度顶点作为起点(欧拉路必须从奇数度顶点开始)
            start = i;
            break;  // 只需要找到一个奇数度顶点即可
        }
    }
    // 如果没有奇数度顶点,说明是欧拉回路,start保持为1

    // 找欧拉路径
    dfs(start);
    // 输出路径
    // 由于路径是逆序存储的,需要从后往前输出
    for (int i = path.size() - 1; i >= 0; i--) {
        cout << path[i];
        if (i > 0) cout << " ";  // 最后一个数后面不加空格
    }
    cout << endl;
    
    return 0;
}


    扫描二维码推送至手机访问。

    版权声明:本文由青少年编程知识记录发布,如需转载请注明出处。

    分享给朋友:

    相关文章

    C++ 中的常量

    C++ 中的常量

    一、说明常量和变量是相对的概念 —— 变量是 “能变化的量”,而常量就是一旦定义就固定不变、不能修改的值。用生活里的例子类比,你就能秒懂为什么需要常量:本质是 “给固定不变的东西贴‘只读标签’,避免误...

    【算法】分治算法

    前言所谓分治算法就是指分而治之,即将较大规模的问题分解成几个较小规模的问题,通过对较小问题的求解达到对整个问题的求解。当我们将问题分解成两个较小问题求解时的分治方法称为二分法。比如,我们玩过最简单的猜...

    指针(三):指针与函数

    1.交换的例子#include<iostream> #include<cstdio> #include<cstring> using namespa...

    C++小项目——实时钟表

    C++小项目——实时钟表

    0.前言在很多游戏中,我们要使用时间,这个时间一个是系统当前的时间,一个是服务器给你的时间。这篇文章我们尝试做一个模拟时钟。效果图如下1.任务分解1.首先我们尝试使用easyx来画一个。基本框架如下:...

    【题解】围圈报数(约瑟夫问题)

    【题解】围圈报数(约瑟夫问题)

    【题目描述】有n个人依次围成一圈,从第1个人开始报数,数到第m个人出列,然后从出列的下一个人开始报数,数到第m个热呢又出列,... ,如此反复到所有的人全部出列为止。设n个人的编号分别为1,2,......

    【题解】组合数学

    【题解】组合数学

    一、排列与组合口诀:有序排列,无序组合,分类相加,分步相乘。1.排列数公式:表示的含义是从n个数中选出m个进行排队,有多少种不同的排法。从n个不同的元素中任取m(m≤n)个元素的所有排列的个数,叫做从...