UVa OJ 117 - The Postal Worker Rings Once (让邮差只走一圈)

Time limit: 3.000 seconds
限时3.000秒

Background
背景

Graph algorithms form a very important part of computer science and have a lineage that goes back at least to Euler and the famous Seven Bridges of Königsberg problem. Many optimization problems involve determining efficient methods for reasoning about graphs.
计算机科学中的一个非常重要的组成部分就是图论算法,它们都可以追溯到欧拉和著名的哥尼斯堡七桥问题。许多优化问题都需要确定有关图推理的高效方法。

This problem involves determining a route for a postal worker so that all mail is delivered while the postal worker walks a minimal distance, so as to rest weary legs.
该问题就是要为邮递员确定一条路线,让他在投递所有信件的同时只走最短的距离,以节省脚力。

 

The Problem
问题

Given a sequence of streets (connecting given intersections) you are to write a program that determines the minimal cost tour that traverses every street at least once. The tour must begin and end at the same intersection.
给定一系列的街道(由路口相互联通),你要写一个程序来确定跑遍所有这些街道所需的最小旅程。旅程结束时必须回到起点的路口。

The "real-life" analogy concerns a postal worker who parks a truck at an intersection and then walks all streets on the postal delivery route (delivering mail) and returns to the truck to continue with the next route.
在现实生活中的情况是一个邮递员将它的卡车停在路口,然后跑遍所有的小巷来完成投递任务(发邮件),最后回到卡车继续下一次投递任务。

The cost of traversing a street is a function of the length of the street (there is a cost associated with delivering mail to houses and with walking even if no delivery occurs).
穿过一条街所花费的开销是这条街长度的函数(将邮件投递入户会产生开销,行走而没有投递也会产生开销)。

In this problem the number of streets that meet at a given intersection is called the degree of the intersection. There will be at most two intersections with odd degree. All other intersections will have even degree, i.e., an even number of streets meeting at that intersection.
在这个问题中,与一个路口相连的所有街道的数量称为这个路口的“度”。最多存在两个奇数度的路口,其它所有路口的度均为偶数,即有偶数条街道与该路口相连。

 

The Input
输入

The input consists of a sequence of one or more postal routes. A route is composed of a sequence of street names (strings), one per line, and is terminated by the string "deadend" which is NOT part of the route. The first and last letters of each street name specify the two intersections for that street, the length of the street name indicates the cost of traversing the street. All street names will consist of lowercase alphabetic characters.
输入由一条或多条邮递路线构成。路线由一系列的街道名称(字符串)来表示,每条街独占一行。字符串“deadend”表示一条路线的结束,但它本身不是路线的一部分。每条街道的第一个和最后一个字母代表了这条街道的两个路口,街道名称的长度即为穿过这条街道所需的开销。所有街道的名称都由小写字母构成。

For example, the name foo indicates a street with intersections f and o of length 3, and the name computer indicates a street with intersections c and r of length 8. No street name will have the same first and last letter and there will be at most one street directly connecting any two intersections. As specified, the number of intersections with odd degree in a postal route will be at most two. In each postal route there will be a path between all intersections, i.e., the intersections are connected.
比如说,一条街道的名称为“foo”表示它的两个路口为“f”和“o”,且长度为3;另一条街道的名称为“computer”表示它的两个路口为“c”和“r”,长度为8。不存在首尾字母相同的街道,两个路口最多由一条街道直接连通。在每条邮递路线中都存在一条路径可以跑遍所有的路口,即所有路口都是连通的。

 

The Output
输出

For each postal route the output should consist of the cost of the minimal tour that visits all streets at least once. The minimal tour costs should be output in the order corresponding to the input postal routes.
对应输入的每条邮递路线,应输出访问其所有街道至少一次的最短路径的开销。最短路径开销应按照输入的邮递路线的相应顺序输出。

 

Sample Input
输入示例

one
two
three
deadend
mit
dartmouth
linkoping
tasmania
york
emory
cornell
duke
kaunas
hildesheim
concord
arkansas
williams
glasgow
deadend

 

Sample Output
输出示例

11
114

 

Analysis
分析

在介绍算法前必须先明确几个概念。按题目的定义,可将十字路口视为节点,街道视为节点之间的边,那么所给出的数据就是一个强连通无向图(即任何边都可正反两个方向通过,且不存在无法到达的节点)。此外题目中还给出奇数度的路口数最多为2,这就满足了欧拉在近300百年前就证明了的一笔画问题(维基百科)的充要条件:奇顶点的数目等于0或2。

等等,题目并没有说奇顶点的数目不能等于1呀?事实上,那是不可能的,因为奇顶点的数目一定是偶数!推理证明如下:最简单的图有n个点,点与点之间没有边,那么所有点的度都为0。如果在这个图中增加一条线段,那么一定会为两个端点各增加1度,所以任何图的度数和都为偶数。假设图中有奇数个奇顶点,那么整个图的度数之和为奇数×奇数=奇数,与前面的推论矛盾,因此一个图中只可能存在偶数个奇顶点。

即然题目所给的图是可以用一笔划来画出的,那么所有街道只走一次就可以走完所有街道。根据欧拉回路的充要条件(证明比较繁琐,恕不赘述)可知,如果图中只有偶顶点,那么必然存在一条从起点出发,走完全部街道后返回原点的路线。如果存在奇顶点,那么要一遍走完所有街道则必须从其中一个奇顶点出发,到另一个奇顶点结束。这样题目的思路就很清晰了。为所有的这一切,感谢欧拉!(维基百科

先将所有街道的长度和作为最短路径长度,如果存在奇顶点路口,还要加上两个奇顶点之间的最短路径。这道题的规模很小,最多26个路口,因此使用复杂度为O(n3)的弗洛伊德算法(维基百科)来解决最短路径问题是非常便捷的。

 

Solution
解答

#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
//主函数
int main(void) {
	//aMat为连通路口的邻接矩阵,aIdHash为路口字母与其编号的对应表
	//nNodeCnt为路口数量,nRout为最小路径长度
	int aMat[26][26], aIdHash[26], nNodeCnt = 0, nRout = 0, nInf = 0xFFFFFF;
	fill(&aMat[0][0], &aMat[26][0], nInf);
	fill(&aIdHash[0], &aIdHash[26], nInf);
	//循环处理每一行输入,结束时将nRout清0
	for (string strName; cin >> strName;) {
		//如果街名为结束符,则按前一阶段的输入数据进行运算
		if (strName == "deadend") {
			//aOdd数组用于记录奇顶点,pOdd是aOdd的指针形式
			int aOdd[2] = {0, 0}, *pOdd = &aOdd[0];
			for (int i = 0; i < nNodeCnt && pOdd != &aOdd[2]; ++i) {
				//统计每一个顶点的相邻顶点数(连通路口数)
				for (int j = 0; j < nNodeCnt; *pOdd += (aMat[i][j++] != nInf));
				//如果发现奇顶点,加入奇顶点数组
				if (*pOdd % 2 != 0) {
					*(pOdd++) = i;
				}
			}
			//如果存在两个奇顶点,则开始Floyd算法,找最短路径
			if (pOdd != aOdd) {
				for (int k = 0, nSum; k < nNodeCnt; ++k) {
					for (int i = 0; i < nNodeCnt; ++i) {
						//为加快运算减少代码,设置临时变量p = aMat[i]
						for (int j = 0, *p = &aMat[i][0]; j < nNodeCnt; ++j) {
							nSum = p[k] + aMat[k][j];
							p[j] = nSum < p[j] ? nSum : p[j];
						}
					}
				}
				nRout += aMat[aOdd[0]][aOdd[1]];
			}
			//输出最小路径长度
			cout << nRout << endl;
			//初始化数据,以便进行下一轮计算
			fill(&aMat[0][0], &aMat[nNodeCnt][0], nInf);
			fill(&aIdHash[0], &aIdHash[26], nInf);
			nRout = nNodeCnt = 0;
			continue;
		}
		//街道名称的首尾字母
		int nF = *strName.begin() - 'a', nL = *(strName.end() - 1) - 'a';
		//验证路口字母是否已经存在,否则添加路口
		nF = (aIdHash[nF] = aIdHash[nF] == nInf ? nNodeCnt++ : aIdHash[nF]);
		nL = (aIdHash[nL] = aIdHash[nL] == nInf ? nNodeCnt++ : aIdHash[nL]);
		//在邻接距阵中添加路口节点
		aMat[nF][nL] = aMat[nL][nF] = strName.length();
		//统计路径长度
		nRout += strName.length();
	}
	return 0;
}

转载于:https://www.cnblogs.com/devymex/archive/2010/08/13/1798823.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
杭州电子科技大学在线评测系统(杭电OJ)中的题目1000-1100是一系列编程题,我将分别进行回答。 1000题是一个简单的入门题,要求计算两个整数的和。我们可以使用一个简单的算法,读取输入的两个整数,然后将它们相加,最后输出结果即可。 1001题是一个稍微复杂一些的题目,要求实现字符串的逆序输出。我们可以使用一个循环来逐个读取输入的字符,然后将这些字符存储在一个数组中。最后,我们可以倒序遍历数组并将字符依次输出,实现字符串的逆序输出。 1002题是一个求最大公约数的问题。我们可以使用辗转相除法来解决,即先求出两个数的余数,然后将被除数更新为除数,将除数更新为余数,直至两个数的余数为0。最后的被除数就是最大公约数。 1003题是一个比较简单的排序问题。我们可以使用冒泡排序算法来解决,即每次比较相邻的两个元素,如果它们的顺序错误就交换它们的位置。重复这个过程直至整个数组有序。 1100题是一个动态规划问题,要求计算给定序列中的最长上升子序列的长度。我们可以使用一个数组dp来保存到达每个位置的最长上升子序列的长度。每当遍历到一个位置时,我们可以将其和之前的位置比较,如果比之前位置的值大,则将其更新为之前位置的值加1,最后返回dp数组的最大值即可。 以上是对杭电OJ1000-1100题目的简要回答,涉及了一些基本的编程知识和算法思想。希望对您有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值