中国邮路算法(中国邮递员问题)(详细)

article/2025/10/8 9:52:31

通路:在无向图中由点边交替组成的序列就是通路(如果这个图是简单的,那么也可以使用点的序列来表示),如果首尾的点相同,则称为一条回路
无向图的连通性:无向图中任意一对点之间均有通路
欧拉通路:从某个顶点出发,将所有的边遍历一遍并且仅经过一遍的通路序列称为欧拉通路,连通的多重图有欧拉回路而无欧拉回路当且仅当它恰有两个奇数度顶点
这里说明了欧拉通路的条件:

图是连通的,没有孤立节点
对于无向图来说,奇数度的顶点为2个,这两个顶点分别是起点以及终点(0个的话就是回路了)
欧拉回路:如果欧拉通路的起点与终点一样,则成为欧拉回路, 连通的多重图具有欧拉回路当且仅当它的每个顶点都有偶数度
则欧拉回路的条件:

图是连通的,没有孤立节点
无向图的每个节点的度数都是偶数度,有向图每个节点的入度等于出度
判断图是否存在欧拉回路
根据判断的条件,首先是判断图的连通性,然后判断图的每个节点的度数是否是偶数就可以了:

开始还要判断是否是欧拉回路。因为这个程序主要是针对欧拉回路的。
因为图中奇度顶点组合成4条边的情形有很多种,所以要分别求出每种组合形成的边的代价,然后从中选出代价最小的边。
另外,在此问题中,边的“添加”的应该理解为从一个奇度点到另一个奇度点之间的路径。
“邮递员”问题的算法描述如下:
(1) 建立街区无向网的邻接矩阵;
(2) 求各顶点的度数;
(3) 求出所有奇度点;
(4) 求出每一个奇度点到其它奇度点的最短路径;
(5) 找出距离最短的添加方案;
(6) 根据最佳方案添加边,对图进行修改,使之满足一笔画的条件;
(7) 对图进行一笔画,并输出;

程序首先输入点与边,和点与边的关系,读取结点个数和边条数以及点与点间的连通关系,并建立街区无向网的邻接矩阵,同时记录各结点度数。
随后遍历各结点,找出其中所有的奇点。如果奇点个数大于2,则需要加边来使奇点数为0;如果奇点正好为两个,则从其中一个点出发,到另一点结束能形成欧拉图,但是不能形成回路,也不能保证从邮局出发;如果奇点数为0,则可以形成欧拉回路。接下来分情况讨论:
当奇点数大于2的时候:通过Floyd算法求任意两点路径的最短距离,并将读入的图的数据更新为最短路径。随后进入extend_edge()函数。通过标记的方法来加边。当某一边的始点与终点均为奇点,并且两点间可连通时,将需要加边的始点和终点以及距离信息存入odd_edge中。随后通过一个深度遍历,判断需要加的边是否为最短的,即确定距离最短的添加方案。随后用dijkstra算法确定从该始点到终点的最短路径。
当奇点数等于2的时候,判断起始点能否为1,若可以,则设置first_id = 1即可。若不能,则无法构成环路。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define min(a,b) ( (a) < (b) ? (a) : (b) )
#define MAX_NODE 100
#define MAX_EDGE 100
#define INF 0x7fffffff      // 表示两点不连通
int degree[1005];
int tree[1005];typedef struct
{int number;   // 标记位int cost;     // 结点间距int dis;      // 结点最近距离
}Node;typedef struct
{int from;     // 边起点int to;       // 边终点int dis;      // 边距离
}Edge;int n, m;                              // 点的个数和边的个数
int total_edge, odd_point;             // 总边数和奇点数
Node map[MAX_NODE][MAX_NODE];          // 结点连接情况
int point[MAX_NODE];                   // 每个结点的度数
int need_add_num, min_count, edge_num; // 需要添加边的个数
Edge odd_edge[MAX_EDGE];
int point_flag[MAX_EDGE];
int min_edge[MAX_EDGE];
int tmp_edge[MAX_EDGE];
int top;
int finish_flag = 0;
int path_stack[MAX_EDGE];int findroot(int x){               //并查集的方式判断图连通性if(tree[x]==-1)return x;else{int temp = findroot(tree[x]);tree[x] = temp;return temp;}
}void Floyd()
{// 比较i到j直达近还是从i到k加上k到j近。添加的结点k放在最外层循环。int i,j,k;for (k = 1; k <= n; k++)for (i = 1; i <= n; i++)if(map[i][k].dis != INF){for (j = 1; j < n; j++)if(map[k][j].dis != INF){map[i][j].dis = map[j][i].dis = min(map[i][j].dis, map[i][k].dis + map[k][j].dis);}}
}void search_edge(int count, int step)
{// 深度还是广度遍历寻找最优方案// step用于记录数量,count记录最短长度// 寻找路径存入了数组中,可通过i访问int i;if (step == need_add_num){if (count < min_count){for (i = 0; i < need_add_num; i++){min_edge[i] = tmp_edge[i];}min_count = count;}return;}int a, b, c;for (i = 0; i < edge_num; i++){a = odd_edge[i].from;b = odd_edge[i].to;c = odd_edge[i].dis;if (point_flag[a] == 1 && point_flag[b] == 1)// 如果两点均未相连{point_flag[a] = point_flag[b] = 0;    // 置标记位为0tmp_edge[step] = i;search_edge(count + c, step + 1);point_flag[a] = point_flag[b] = 1;}}
}void dijkstra_to_add_edge(int s, int e)
{int i;int dis[MAX_NODE];       // 点到起始(s)点的距离int used[MAX_NODE];      // 标记位int pre[MAX_NODE];       // 记录其从哪一点过来的,方便回溯memset(used, 0, sizeof(used));   // 初始化一波for (i = 1; i <= n; i++){dis[i] = INF;}dis[s] = 0;while (1){int v = -1;for ( i = 1; i <= n; i++){if (!used[i] && (v == -1 || dis[i] < dis[v])){v = i;}}if (v == e || v == -1)// 当当前点是末点e时或本身v就是最小时break;used[v] = 1;    // 修改标记位for (i = 1; i <= n; i++){if (map[v][i].cost < INF && (dis[v] + map[v][i].cost) < dis[i]){pre[i] = v;dis[i] = dis[v] + map[v][i].cost;}}}int v = e;int pre_node = e;while (pre_node != s){pre_node = pre[v];++map[pre_node][v].number;    // 加边++map[v][pre_node].number;}
}void extend_edge(int add_num)
{int i,j;need_add_num = add_num;memset(point_flag, 0, sizeof(point_flag));edge_num = 0;// 当两个点都是奇点的时候for (i = 1; i < n; i++){if ((point[i] & 0x1) == 1){for (j = i+1; j <= n; j++){if ((point[j] & 0x1) == 1 && map[i][j].dis < INF){point_flag[i] = point_flag[j] = 1;   // 将i,j两点标记为需要被连接的奇点odd_edge[edge_num].from = i;         // 将相关信息存入odd_edge数组中odd_edge[edge_num].to = j;odd_edge[edge_num].dis = map[i][j].dis;edge_num++;}}}}min_count = INF;    // 设置最小值,方便比较search_edge(0, 0);if (min_count < INF){int a, b;for (i = 0; i < need_add_num; i++){int k = min_edge[i];a = odd_edge[k].from;b = odd_edge[k].to;dijkstra_to_add_edge(a, b);   // 用dijkstra算法求加边后两点最短距离point[a] = point[b] = 0;}odd_point -= add_num * 2;total_edge += add_num;}else{exit(-1);}}void search_euler_path(int s)
{int i;for (i = 1; i <= n; i++){if (map[s][i].number > 0){--map[s][i].number;--map[i][s].number;path_stack[top++] = i;if (top == (total_edge + 1))// 结点比总边数多1{finish_flag = 1;return;}search_euler_path(i);if (finish_flag)return;++map[s][i].number;++map[i][s].number;--top;}}
}int main()
{int i,j;printf("请输入顶点个数和边的条数:\n");while (scanf("%d %d", &n, &m) != EOF){memset(tree, -1, sizeof(tree));memset(degree, 0, sizeof(degree));// 初始化memset(point, 0, sizeof(point));for (i = 1; i <= n; i++)for (j = 1; j <= n; j++)map[i][j].cost = map[i][j].dis = INF;total_edge = 0;// 读入图的数据printf("请输入顶点和边的关系:\n");int a, b, c;for (i = 0; i < m; i++){scanf("%d %d %d", &a, &b, &c);int tempa = findroot(a);int tempb = findroot(b);if(tempa != tempb)tree[tempa] = tempb;degree[a]++;                //无向图记录度数degree[b]++;map[a][b].cost = map[b][a].cost = c;map[a][b].dis = map[b][a].dis = c;map[a][b].number = map[b][a].number = 1;++point[a];++point[b];++total_edge;}int flag = 0;int ans = 0;for(i=1; i<= n; i++){     //判断连通性if(tree[i]==-1)ans++;}for( i=1; i<=n; i++){    //判断度数if(degree[i]%2){flag = 1;break;}}if(ans > 1 || flag){printf("不是欧拉回路\n");return 0;}elseprintf("是欧拉回路\n");odd_point = 0;for ( i = 1; i <= n; i++)// 判断点是否为奇点{if ((point[i] & 0x1) == 1){odd_point++;}}int first_id = 1;    // 设置邮局的位置 1即为Aif (odd_point > 2)// 奇点个数大于两个的时候,用floyd算法更新任意两点之间最短路径,并且添加边{Floyd();extend_edge(odd_point / 2);// 两个奇点添加一条边,共需要添加odd_point/2条边}top = 0;if (odd_point == 2)// 奇点个数为2的时候,从一个奇点出发到另一个奇点去,但不能构成环路,直接退出程序{for (i = 1; i <= n; i++){if ((point[i] & 0x1) == 1){first_id = i;break;}}if (first_id != 1){exit(-2);}}path_stack[top++] = first_id;// 利用栈进行深度搜索来确定最短路线并存入数组search_euler_path(first_id);char vex;// 将最短路线输出for (i = 0; i <= total_edge; i++){vex = path_stack[i] + 'A' - 1;printf("%c", vex);if (i < total_edge){printf("->");}}printf("\n");}return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


http://chatgpt.dhexx.cn/article/mQGmuTmD.shtml

相关文章

那些有趣的网站

之前分享过那些有意思的网站 &#xff0c;这里继续分享一波&#xff0c;也许你用得上。 福利单词 一边背单词一边看妹子的网站&#xff0c;用电脑打开&#xff0c;配合ctrlw 关闭新窗口&#xff0c;不知不觉就背了百来个词了 https://easychen.gitee.io/foxdict/ 工资计算器 简…

常用又有趣的网站大合集

〇、【Python challenge】通关代码及攻略 一、PIECES 拼图 PIECES 拼图网站用 30 个 CSS 碎片进行拼图&#xff0c;向我们呈现了 30 种濒临灭绝的动物。 二、小甲鱼编程学习工作室 包含了各种编程语言学习以及计算机基本操作的教学与奇技淫巧 三、Wolfram Alpha 这是由Wol…

超酷的13个CSS有趣学习网站

13个CSS有趣学习网站 今天来给大家推荐13个辅助你学习巩固知识的网站&#xff0c;让你边玩边学边记&#xff01; 因为这些网站大多都是国外的大佬们做的&#xff0c;所以网页大多都是英文&#xff0c;为了更好地使用&#xff0c;给你们推荐两个翻译的方式&#xff1a; 使用C…

电脑技巧:盘点10个非常实用且有趣的网站

目录 1、聆听大海的声音 2、在线生成Logo 3、今日热榜 4、十万个为什么&#xff08;大人版&#xff09; 5、视频创作导航 6、改图鸭 7、好看电影推荐 8、童年游戏合集 9、各种沙雕表情包 10、反向词典 今天给大家分享10个非常实用且有趣的网站&#xff0c;值得收藏&a…

科普:如何找到有趣的网站?

原址&#xff1a;http://shedingkong.lofter.com/post/302b9d_1943cf2# 其实我发各种网站的推荐&#xff0c;是为了给自己留用&#xff0c;也是因为现在的审查环境下&#xff0c;各种下载资源的分享十分痛苦。总有人问我怎么知道这么多网站&#xff0c;这里来介绍几种方法。 方…

你是否看到过如此有趣的AI网站?

1、营销文案&#xff1a;CopyAI: Create Marketing Copy In Seconds 2、美化ppt设计&#xff1a;https://www.beautiful.ai/ 3、图片修改&#xff1a;https://hotpot.ai 4、照片变视频&#xff1a;https://www.myheritage.com/deep-nostalgia 美化头像&#xff1a;Free Profil…

有趣好玩的python编程网站

✅作者简介&#xff1a;大家好我是hacker707,大家可以叫我hacker &#x1f4c3;个人主页&#xff1a;hacker707的csdn博客 &#x1f525;系列专栏&#xff1a;python &#x1f4ac;推荐一款模拟面试、刷题神器&#x1f449;点击跳转进入网站 整理了一些非常有意思&#xff0c;适…

有趣的表白网站

今天看到一个傻瓜式的表白网址生成网站&#xff0c;觉得还挺有意思的&#xff0c;所以来分享一下。http://www.51bbw.cn/show/ 进入网址就会看到下面的界面&#xff1a; 然后就可以“量力而行”了&#xff0c;依据财力选择你想制作的模板&#xff0c;老白嫖怪一眼看中了“免费…

有趣的网站

黑客帝国文字雨生成器 AirPano 足不出户的旅行 创客贴 自媒体制图神器

实时查看Starlink在轨卫星、地面站数目和分布情况的有趣网站

记录一些SpaceX的Starlink相关的一些有趣有用小网站~ 一、实时查看Starlink在轨卫星数目和分布情况 https://satellitemap.space/ 除了可以看Starlink的&#xff0c;还可以看OneWeb和GPS的在轨卫星和分布情况&#xff1b; 截止到2022-4-23&#xff0c;共有卫星2283颗&#xf…

收藏全球最有趣的网站 (上)

在线玩转指尖陀螺-FFFFidget ←点击评分&#xff0c;有趣指数&#xff1a; 3.40星 风靡全球的指间小玩具&#xff01;网站可以在线体验指尖陀螺的迷之旋转&#xff0c;同时支持PC和移动端&#xff0c;手机玩起来更带感&#xff01; 传送门 http://ffffidget.com/ 继续阅读 →…

几个有创意有趣的网站推荐

几个有趣的网站推荐&#xff0c;这些网站很有创意&#xff0c;第一眼就很惊艳 都能打开&#xff0c;不过速度有点慢&#xff0c;一定要有耐心等待&#xff0c;因为有几个是国外的网站&#xff0c;如果是在手机上打开的&#xff0c;复制地址进浏览器 1、Marco Gomez &#xff0d…

记录一些有趣网站

https://css-tricks.com/ 2018.01.15更 腾讯游戏官方设计团队 http://tgideas.qq.com/?ADTAGmedia.gameweb.console 2018.01.16更 该文档库&#xff1a;http://tgideas.qq.com/doc/ 里面有个相关的一些干货 王者荣耀网站帮助管理平台 https://pvp.qq.com…

一个神奇的资源网站「有趣网站收藏家」共有186个站点资源-北忘山修改版

一个神奇的资源网站「有趣网站收藏家」共有186个站点资源-北忘山修改版 网站介绍 这个网站有点东西&#xff0c;目前收集了186个资源&#xff0c;小北当时拔下来之后发现都是作者纯html手写的&#xff0c;意思就是每每添加网站都是手动添加上去。小北整下来之后&#xff0c;做了…

有趣的网站合集

导航页面 1、有趣网址之家 – 收藏全球最有趣的网站 https://youquhome.com/ 一、电子图书及公开课程 1、国图公开课 依托国家图书馆宏富的馆藏资源&#xff0c;以服务国家战略、传播中华优秀传统文化、提高公众文化生活品质为主线。一方面整合多种文献资源&#xff0c;并结…

有趣网站盲盒项目设计

嗯&#xff0c;在这里再发一遍&#xff0c;期待有更多流量吧~ 2023-07-01:因docker误删&#xff0c;导致容器丢失&#xff0c;虽然gitee上有代码&#xff0c;数据库也有备份&#xff0c;重新恢复应该不是问题&#xff0c;但是博主二赛君不想折腾了&#xff0c;精力收敛&#xf…

9个超级有趣好玩的网站,打开就停不下来

分享9个让你玩了还想玩的小网站&#xff0c;涵盖上百个小游戏、听歌、绘画等&#xff0c;随便一个都能用来摸鱼打发时间&#xff0c;强烈建议大家收藏&#xff01; 1、查找不动的表情包 一个找茬小游戏网站&#xff0c;在众多表情包中找出一直不动的表情包&#xff0c;然后点…

10个好玩到爆的网站,打开就能玩,个个超有趣

正所谓“你摸鱼&#xff0c;我摸鱼&#xff0c;老板奔驰变青桔”&#xff0c;上班太无聊想要玩一些好玩的网站来打发时间&#xff1f;那今天的分享可千万别错过&#xff01;下面这10个超级有趣的网站&#xff0c;随便一个都可以玩一天。 1、世界名画在线拼图 一个加勒里克斯在…

11个好玩有趣的网站,一打开就停不下来

分享11个好玩有趣的摸鱼网站&#xff0c;有趣味类也有游戏类&#xff0c;大家可以根据自己的办公环境选择适合自己的网站摸鱼噢~ 趣味类 1、今日热榜 一个聚合热榜合集&#xff0c;它里面收录了微博、微信、抖音、知乎、B站、虎扑、天涯等多个网站的热搜内容&#xff0c;直接点…

9个冷门但有趣的网站,每一个都不想错过

大家平时知道的比较多的除了办公网站&#xff0c;最多的应该就是视频网站了&#xff0c;除了这两种以外&#xff0c;其实还有很多好玩很有趣的网站&#xff0c;每个都超有意思&#xff0c;今天就来跟大家分享9个&#xff0c;看看有没有你喜欢的。 1、世界名画在线拼图 一个网上…