UVa OJ 116 - Unidirectional TSP (单向旅行商问题)

Time limit: 3.000 seconds
限时:3.000秒

 

Background
背景

Problems that require minimum paths through some domain appear in many different areas of computer science. For example, one of the constraints in VLSI routing problems is minimizing wire length. The Traveling Salesperson Problem (TSP) -- finding whether all the cities in a salesperson's route can be visited exactly once with a specified limit on travel time -- is one of the canonical examples of an NP-complete problem; solutions appear to require an inordinate amount of time to generate, but are simple to check.
在计算机科学的很多领域中都会出现查找最短路径的问题。比如,在超大规模集成电路(VLSI)布线问题中的一个约束条件就是要使线路最短。旅行商问题(TSP)——判断是否存在一条路线可以让赶集的商人跑遍所有城市且都只去一次——就是一个NP完全问题的教科书例子,生成解需要花费大量的时间,但很容易检测一个解是否正确。

This problem deals with finding a minimal path through a grid of points while traveling only from left to right.
该问题是要在一组点的格网中找到一条从左至右的最小路径。

 

The Problem
问题

Given an m × n matrix of integers, you are to write a program that computes a path of minimal weight. A path starts anywhere in column 1 (the first column) and consists of a sequence of steps terminating in column n (the last column). A step consists of traveling from column i to column i+1 in an adjacent (horizontal or diagonal) row. The first and last rows (rows 1 and m) of a matrix are considered adjacent, i.e., the matrix "wraps" so that it represents a horizontal cylinder. Legal steps are illustrated below.
给定一个m × n的整数矩阵,你要写一个程序来计算出最小权值。一条路径从列1(第一列)的任意位置开始,包括一系列的移动,最终抵达列n(最后一列)。每一步均由列i到列i+1的相邻行(水平或对角)。 矩阵的第一行和最后一行(行1和m)认为是相邻的,也就是说矩阵是环绕的,就像一个水平的圆柱体。合法的移动图示如下。

 

fig1

 

The weight of a path is the sum of the integers in each of the n cells of the matrix that are visited.
路径的权重就是矩阵中遍例到的所有n个单元的整数值之和。

For example, two slightly different 5 × 6 matrices are shown below (the only difference is the numbers in the bottom row).
比如,两个稍有不同的5 × 6的矩阵图示如下(仅仅在最下一行的数字中有一处不同)。

 

UVa OJ 116 - Unidirectional TSP (单向旅行商问题)_第1张图片

The minimal path is illustrated for each matrix. Note that the path for the matrix on the right takes advantage of the adjacency property of the first and last rows.
两个矩阵的最短路径如图所示。注意右边矩阵的最短路径利用了最上一行和最下一行的特殊相邻性质。

 

The Input
输入

The input consists of a sequence of matrix specifications. Each matrix specification consists of the row and column dimensions in that order on a line followed by m × n integers where m is the row dimension and n is the column dimension. The integers appear in the input in row major order, i.e., the first n integers constitute the first row of the matrix, the second n integers constitute the second row and so on. The integers on a line will be separated from other integers by one or more spaces. Note: integers are not restricted to being positive. There will be one or more matrix specifications in an input file. Input is terminated by end-of-file.
输入由一系列矩阵的定义构成。每个矩阵的定义包括行(m)和列(n)的维数,以及下面的m × n个整数。输入的整数按行排列,即输入的第一行n个整数为矩阵的第一行,第二行n个整数为矩阵的第二行,以此类推。一行中的整数由一个或多个空格隔开。注意:整数并不限定为正。输入的数据中会有一个或多个矩阵,由EOF标志结束。

For each specification the number of rows will be between 1 and 10 inclusive; the number of columns will be between 1 and 100 inclusive. No path's weight will exceed integer values representable using 30 bits.
每个定义中行数在1到10之间(含);列数在1到100之间(含)。不会存在超过30位整数表示范围的路径权值。

 

The Output
输出

Two lines should be output for each matrix specification in the input file, the first line represents a minimal-weight path, and the second line is the cost of a minimal path. The path consists of a sequence of n integers (separated by one or more spaces) representing the rows that constitute the minimal path. If there is more than one path of minimal weight the path that is lexicographically smallest should be output.
每个输入的矩阵定义对应两行输出,第一行表示一条最小权重路径,第二行输出路径的权重值。路径由n个整数组成(由一个或多个空格隔开)表示组成最小路径的行号。如果有多于一条路径的权重同为最小,则按字典顺序输出最小的一组。

 

Sample Input
输入示例

5 6
3 4 1 2 8 6
6 1 8 2 7 4
5 9 3 9 9 5
8 4 1 3 2 6
3 7 2 8 6 4
5 6
3 4 1 2 8 6
6 1 8 2 7 4
5 9 3 9 9 5
8 4 1 3 2 6
3 7 2 1 2 3
2 2
9 10 9 10

 

Sample Output
输出示例

1 2 3 4 4 5
16
1 2 1 5 4 5
11
1 1
19

 

Analysis
分析

典型且简单的动态规划(DP)问题,如果对迪科斯彻最短路径算法比较熟悉的话,解这道题应该是驾轻就熟了。大体的思路是,从第2列开始依次遍例每一列,对于当前列每行的值都找出可从上一列到达该列的所有路径的最小权值,并与该值相加后更新到该值上。这句话比较拗口,举例说明:假设一个5行的距阵,mi, j表示矩阵第i行第j列的值。设当前列为j,上一列为j-1,m1, j-1到m5, j-1的值依次为2、3、1、4、5,m1,j到m5, j的值依次为3、5、2、1、4。按照题意,在上一列中,只有m5,j-1、m1,j-1和m2,j-13个节点可以到达m1,j。在这3个节点中,显然m1,j-1 = 2的值最小,那么就令m1, j <- m1,j + m1,j-1,依次更新新当前列的所有节点。当所有列都这样处理完成,最后一列的最小值就是要求的最短路径长度。算法时间复杂度为O(n),n为矩阵节点数。

该算法和迪科斯彻算法类似,利用了动态规划的思想,将求整个矩阵最短路径的问题按列分解成多个子问题。每处理一列都得到了从最初的一列到当前列的最短路径长度,但要求得具体路径还需在更新每一个节点时记录当前的最短路径是来自于上一列的哪个节点。在完成遍例后则可根据记录的“上一节点”数组回溯得到整个路径。

这道题有一个需要注意的地方,就是题目要求:如果存在多条路径的最短路径长度都相等,按字母表顺序取路径较小的一条作为输出。因此在更新节点路径长度值时,如果出现两个“上一节点”同为最小,就要取行号较小(靠上)的一个。字母表顺序是从第一个输出开始比较,因此需要从后向前遍例,否则不能得到正确的结果。

 

Solution
解答

#include <algorithm>
#include <iostream>
using namespace std;
//主函数
int main(void) {
	//aMat用于存储距阵,aPrev存储上一列(前一步)的行号
	//为方便运算,这里采用列在前行在后的维度排列
	int aMat[100][10], aPrev[100][10];
	//循环处理所有矩阵
	for (int nRows, nCols; cin >> nRows >> nCols; ) {
		//循环读入矩阵
		for (int i = 0; i < nRows; ++i) {
			for (int j = 0; j < nCols; cin >> aMat[j++][i]);
		}
		//从后向前循环遍例每一例
		for (int j = nCols - 2; j >= 0; --j) {
			for (int i = 0; i < nRows; ++i) {
				//为加快速度,减少代码,为数组元素设置临时指针变量
				int *p = &aMat[j + 1][0];
				//设最小值所在行号为上面一行(右上方一格)
				int nMin = (i - 1 + nRows) % nRows, d = (i + 1) % nRows;
				//与右方一格比较,取最小值。若相等按字母顺序排列
				if (p[i] < p[nMin] || (p[i] == p[nMin] && i < nMin)) {
					nMin = i;
				}
				//与右下方一格比较,取最小值。若相等按字母顺序排列
				if (p[d] < p[nMin] || (p[d] == p[nMin] && d < nMin)) {
					nMin = d;
				}
				//更新当前一列的值为从最末一列到当前列的最小值
				aMat[j][i] += p[nMin];
				//记录上一步的行号
				aPrev[j][i] = nMin;
			}
		}
		//检查开头一列的最小值,即最小路径总权重
		int nMin = min_element(&aMat[0][0], &aMat[0][nRows]) - &aMat[0][0];
		int nWeight = aMat[0][nMin];
		//避免在行尾输出多余空格,先输出第1个值
		cout << nMin + 1;
		//循环输出路径(各列行号)
		for (int j = 1; j < nCols; ++j) {
			//获取下一步的行号并输出
			nMin = aPrev[j - 1][nMin];
			cout << ' ' << nMin + 1;
		}
		//输出路径总权重
		cout << '\n' << nWeight << endl;
	}
	return 0;
}

你可能感兴趣的:(UVa OJ 116 - Unidirectional TSP (单向旅行商问题))