当前位置:首页 > C++知识 > 正文内容

一笔画问题


【题目描述】

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

根据一笔画的两个定理,如果寻找欧拉回路,对任意一个点执行深度优先遍历;找欧拉路,则对一个奇点执行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;
}


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

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

分享给朋友:
返回列表

上一篇:图的遍历

没有最新的文章了...

相关文章

【数据结构】队列—基本概念

【数据结构】队列—基本概念

一、基本定义队列是一种先进先出的线性结构,简称FIFO结构。特点就是“先进先出”二、队列的相关概念队头与队尾:允许元素插入的一端称为队尾,允许元素删除的一端称为队头入队:队列的插入操作出队:队列的删除...

C++中双冒号(::)的用法

一、作用域符号前面一般是类名称,后面一般是该类的成员名称,C++为例避免不同的类有名称相同的成员而采用作用域的方式进行区分如:A,B表示两个类,在A,B中都有成员member。那么A::member就...

【题解】奶牛的回音

【题目描述】奶牛们灰常享受在牛栏中牟叫,因為她们可以听到她们牟声的回音。虽然有时候并不能完全听到完整的回音。Bessie曾经是一个出色的秘书,所以她精确地纪录了所有的牟叫声及其回声。她很好奇到底两个声...

【数据结构】并查集1

【数据结构】并查集1

1.引入    对于一个集合S={a1, a2, …, an-1, an},我们还可以对集合S进一步划分: S1,S2,…,Sm-1,Sm,我们希望能够快速确定...

【STL】二分查找函数(算法)—binary_search

【说明】binary_search() 实现了一个二分查找算法。它会在前两个参数指定范围内搜索等同于第三个参数的元素。指定范围的迭代器必须是正向迭代器而且元素必须可以使用 < 运算符来比较。这个...

DEVC++中的断点调试

DEVC++中的断点调试

1.调试程序的两种方法编程的时候经常会遇到自己的输出结果跟标准结果或者预期的结果不一样,这个时候就要用到调试程序的功能。调试程序的目的有两个,一个是找出程序中的错误,另一个是监视变量的变化。2.DEV...