首页 新闻 会员 周边 捐助

最优路径算法求解

2
悬赏园豆:200 [已解决问题] 解决于 2012-01-31 17:27

一道算法题,百思不得其解,请高手指破迷津。我觉得此题有一定的实用意义,所以值得研究一下。此题有三大难点,不仅要找到最短路径,还要找到最短路径中的最大分值,另外还得找得快,这三点加在一起很有难度。此题和最短路径问题之间略有区别,首先这不是任意两点之间的最短路径,而是两个位置固定的顶点之间的最短路径,另外这里只有上下左右四个方向,没有斜向移动,还有就是相邻两点之间的长度均为1,也就是说不需要考虑权值,因此在最短路径这方面A*似乎并不合适。但这个问题要求不仅要找到一条最短路径,还要找到所有的最短路径,并在分值最大的路径中选一条。如果没有障碍,用动态规划很容易解决,但这个还必须考虑障碍的问题。还有最重要的一点是查找速度要快。能够写出完整可运行的代码最好,不写代码只阐述解题的方法或思路也行,既有代码又有原理的最完美。敬请各位大师不吝赐教,越详细越好。

 


 

题目:

最优路径选择

 

描述:

公主被坏人囚禁在城堡B处,王子从A处出发进行解救。要求找到最佳解救路线。

1. 地图为边长大于等于2的方形(长方形或正方形),公主永远位于右下角的B点(坐标为MaxX,MaxY),王子永远从A点出发(坐标为0,0)。

2. 路途中坏人有守卫,路线上也有路障,用数值代表优先级。-1代表路障,不可通行。9代表没有守卫,0则代表守卫森严。0~9分为10个等级,均可通行,但数值越大越应该优先选择。

3. 地图中可能完全没有可通行的路线,也就是说由于受到路障的阻碍,王子根本无法营救公主。

 

示例图:

图中的红色路线就是应该被选中的路线。

Sample

 

要求:

1. 王子必须走最短的路线,也就是步数要最小,当然这就意味着不能走重复或交叉的路线。

2. 如果存在多条最短路线,要走最容易的那条,也就是路线的总分值要最大。

3. 如果最短路线中分值最大的路线仍然有多条,可任意选择一条即可。

4. 如果没有任何一条路线可行,应该给出提示,不能进入死循环。

5. 地图应至少支持到1000x1000,可用随机数来测试大地图,速度越快越好。

 

测试用数据:

1. 普通地图 { { 0, 7, 8, 8, 6, 6, 2, -1, -1, -1, 7, 9, 3, 9, 5 }, { 0, 0, -1, -1, 7, 2, 6, -1, -1, -1, 6, 3, 8, 4, 5 }, { 5, 6, -1, -1, 1, 2, 3, 2, 8, 2, 5, 0, 4, 1, 5 }, { 2, 5, -1, -1, 8, 2, 0, 7, 3, 6, 2, 8, -1, -1, -1 }, { 2, 3, 5, 2, 3, -1, -1, 5, 7, -1, -1, 7, -1, -1, -1 }, { 1, 2, 5, 5, 6, -1, -1, 8, 5, -1, -1, 8, -1, -1, -1 }, { 3, 4, 3, 8, 0, 1, 4, 1, 7, -1, -1, 9, 4, 6, 1 }, { 4, 2, 6, 1, 4, 0, 2, 2, 1, -1, -1, -1, -1, 8, 8 }, { 0, -1, -1, 8, -1, -1, 2, 9, 0, -1, -1, -1, -1, 0, 0 }, { -1, -1, -1, 3, 7, -1, 2, 6, 4, 7, 0, 2, 2, 9, 8 }, { -1, -1, -1, -1, 3, 7, 3, 5, 8, 9, -1, 2, 5, 0, 7 }, { -1, -1, -1, -1, -1, 4, 1, 5, 0, 6, 8, -1, -1, 0, 6 }, { 8, -1, -1, 1, 2, 2, -1, -1, -1, 2, 6, -1, -1, -1, 5 }, { 8, 6, 5, 6, 6, 5, -1, -1, -1, 3, 4, 7, -1, -1, 4 }, { 7, 3, 4, 0, 0, 0, -1, -1, -1, 4, 2, 6, 4, 6, 0 } }

Test1

2. 迷宫式地图 { { 0, 6, 3, 0, 9, 4, 5, 5, -1, 7, 6, 1, 2, 6, 6 }, { 9, -1, -1, 6, -1, 5, 2, -1, -1, -1, 1, -1, 1, 0, 3 }, { 7, -1, 4, 3, -1, 1, -1, 4, 3, 1, 6, 2, -1, -1, 4 }, { 0, -1, 2, 8, 2, 9, -1, 9, -1, 2, -1, 2, -1, 6, 1 }, { 8, 4, -1, -1, 0, -1, 7, -1, 1, 4, 8, -1, 1, 4, -1 }, { 2, -1, 4, 6, 2, 5, 2, -1, 7, 3, 8, -1, 1, -1, 4 }, { 8, 7, 8, 1, -1, 1, 7, -1, 8, 6, -1, 5, 0, -1, 3 }, { 1, -1, -1, 6, 6, -1, 7, 5, 2, -1, 6, 1, -1, 2, 0 }, { 7, 4, 5, -1, 5, -1, 8, -1, 1, -1, 4, -1, 5, 3, 1 }, { 0, -1, 7, -1, 1, 9, -1, 4, 5, -1, 0, 1, 4, -1, 0 } }

Test2

3. C#随机生成地图

int[,] ByteList = new int[Height, Width];
Random Rn = new Random();
for (int i = 0; i <= Height; i += 1)
{
for (int j = 0; j <= Width; j += 1)
{
ByteList[i, j] = (int)(Rn.NextDouble() * 11) - 1;
}
}


输出格式:

1. 计算路线所用的时间,单位为毫秒。

2. 路线的步数。

3. 路线的分值。

4. 标出路线上的每个点。

5. 使用VB.NET或C#编程,用函数返回一个string,以便将string显示到一个TextBox中。string中应包含以上各项信息,各项之间请用空行隔开。

WinStudy的主页 WinStudy | 初学一级 | 园豆:20
提问于:2012-01-28 09:21
< >
分享
最佳答案
0

我也做了好久,才得出下面的代码:

PathFinder.cs

  1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Drawing;
6
7
8 namespace MatrixPath
9 {
10 public struct MatrixNode
11 {
12 public Size Direction;
13 public int Depth;
14 public int Cost;
15 };
16
17 public class PathFinder
18 {
19 public bool Success;
20 public TimeSpan Elapsed;
21 private Point StartPoint = new Point(0,0);
22 private Point EndPoint;
23
24 private int[,] matrix;
25 public int[,] Matrix
26 {
27 get {
28 return this.matrix;
29 }
30 set {
31 this.matrix = value;
32 if (this.matrix == null)
33 {
34 this.Nodes = null;
35 }
36 else
37 {
38 this.Nodes = new MatrixNode[this.matrix.GetUpperBound(0) + 1, this.matrix.GetUpperBound(1) + 1];
39 this.EndPoint = new Point(this.matrix.GetUpperBound(1), this.matrix.GetUpperBound(0));
40 }
41 }
42 }
43 public MatrixNode[,] Nodes {get; set;}
44
45 public bool FindPath()
46 {
47 Size[] directions = {new Size(-1, 0), new Size(0, 1), new Size(1, 0), new Size(0, -1)};
48
49 Success = false;
50 DateTime startTime = DateTime.Now;
51 HashSet<Point> pointSet = new HashSet<Point>();
52 pointSet.Add(StartPoint);
53
54 int depth = 0;
55 while (pointSet.Count > 0)
56 {
57 Point[] points = pointSet.ToArray<Point>();
58 pointSet.Clear();
59 depth++;
60
61 foreach(Point lastPoint in points)
62 {
63 foreach(Size direction in directions)
64 {
65 Point nextPoint = lastPoint + direction;
66 if (nextPoint == StartPoint || nextPoint.Y < 0 || nextPoint.X < 0 || nextPoint.Y > EndPoint.Y || nextPoint.X > EndPoint.X || -1 == Matrix[nextPoint.Y, nextPoint.X])
67 continue;
68 if (Nodes[nextPoint.Y, nextPoint.X].Depth > 0)
69 continue;
70 pointSet.Add(nextPoint);
71 Success = Success || nextPoint == EndPoint;
72 if (Nodes[nextPoint.Y, nextPoint.X].Cost < Nodes[lastPoint.Y, lastPoint.X].Cost + Matrix[nextPoint.Y, nextPoint.X])
73 {
74 Nodes[nextPoint.Y, nextPoint.X].Direction = direction;
75 Nodes[nextPoint.Y, nextPoint.X].Cost = Nodes[lastPoint.Y, lastPoint.X].Cost + Matrix[nextPoint.Y, nextPoint.X];
76 }
77 }
78 }
79 foreach (Point point in pointSet)
80 {
81 Nodes[point.Y, point.X].Depth = depth;
82 }
83 }
84 Elapsed = DateTime.Now - startTime;
85 return Success;
86 }
87
88 public string ResultString
89 {
90 get
91 {
92 if (!Success)
93 return null;
94 //1. 计算路线所用的时间,单位为毫秒。
95 //2. 路线的步数。
96 //3. 路线的分值。
97 //4. 标出路线上的每个点。
98 //5. 使用VB.NET或C#编程,用函数返回一个string,以便将string显示到一个TextBox中。string中应包含以上各项信息,各项之间请用空行隔开。
99 StringBuilder sb = new StringBuilder();
100
101 Point point = EndPoint;
102 sb.AppendFormat("({0,2},{1,2}): {2,2}", point.Y, point.X, Matrix[point.Y, point.X]);
103 while (true)
104 {
105 point = point - Nodes[point.Y, point.X].Direction;
106 sb.Insert(0, string.Format("({0,2},{1,2}): {2,2} \r\n", point.Y, point.X, Matrix[point.Y, point.X]));
107 if (point == StartPoint)
108 break;
109 }
110 sb.Insert(0, string.Format("耗时(毫秒): {0}\r\n路线步数: {1}\r\n路线分值: {2}\r\n", Elapsed.Milliseconds, Nodes[EndPoint.Y, EndPoint.X].Depth, Nodes[EndPoint.Y, EndPoint.X].Cost));
111 return sb.ToString();
112 }
113 }
114 }
115 }

Main函数:

 1         static void Main(string[] args)
2 {
3 PathFinder finder = new PathFinder();
4 //finder.Matrix = new int[,] {{0, 7, -1, 1, 6}, {8, 2, 0, 5, 4}, {2, 3, 9, -1, 0}};
5 //finder.Matrix = new int[,] { { 0, 7, 8, 8, 6, 6, 2, -1, -1, -1, 7, 9, 3, 9, 5 }, { 0, 0, -1, -1, 7, 2, 6, -1, -1, -1, 6, 3, 8, 4, 5 }, { 5, 6, -1, -1, 1, 2, 3, 2, 8, 2, 5, 0, 4, 1, 5 }, { 2, 5, -1, -1, 8, 2, 0, 7, 3, 6, 2, 8, -1, -1, -1 }, { 2, 3, 5, 2, 3, -1, -1, 5, 7, -1, -1, 7, -1, -1, -1 }, { 1, 2, 5, 5, 6, -1, -1, 8, 5, -1, -1, 8, -1, -1, -1 }, { 3, 4, 3, 8, 0, 1, 4, 1, 7, -1, -1, 9, 4, 6, 1 }, { 4, 2, 6, 1, 4, 0, 2, 2, 1, -1, -1, -1, -1, 8, 8 }, { 0, -1, -1, 8, -1, -1, 2, 9, 0, -1, -1, -1, -1, 0, 0 }, { -1, -1, -1, 3, 7, -1, 2, 6, 4, 7, 0, 2, 2, 9, 8 }, { -1, -1, -1, -1, 3, 7, 3, 5, 8, 9, -1, 2, 5, 0, 7 }, { -1, -1, -1, -1, -1, 4, 1, 5, 0, 6, 8, -1, -1, 0, 6 }, { 8, -1, -1, 1, 2, 2, -1, -1, -1, 2, 6, -1, -1, -1, 5 }, { 8, 6, 5, 6, 6, 5, -1, -1, -1, 3, 4, 7, -1, -1, 4 }, { 7, 3, 4, 0, 0, 0, -1, -1, -1, 4, 2, 6, 4, 6, 0 } };
6 finder.Matrix = new int[,] { { 0, 6, 3, 0, 9, 4, 5, 5, -1, 7, 6, 1, 2, 6, 6 }, { 9, -1, -1, 6, -1, 5, 2, -1, -1, -1, 1, -1, 1, 0, 3 }, { 7, -1, 4, 3, -1, 1, -1, 4, 3, 1, 6, 2, -1, -1, 4 }, { 0, -1, 2, 8, 2, 9, -1, 9, -1, 2, -1, 2, -1, 6, 1 }, { 8, 4, -1, -1, 0, -1, 7, -1, 1, 4, 8, -1, 1, 4, -1 }, { 2, -1, 4, 6, 2, 5, 2, -1, 7, 3, 8, -1, 1, -1, 4 }, { 8, 7, 8, 1, -1, 1, 7, -1, 8, 6, -1, 5, 0, -1, 3 }, { 1, -1, -1, 6, 6, -1, 7, 5, 2, -1, 6, 1, -1, 2, 0 }, { 7, 4, 5, -1, 5, -1, 8, -1, 1, -1, 4, -1, 5, 3, 1 }, { 0, -1, 7, -1, 1, 9, -1, 4, 5, -1, 0, 1, 4, -1, 0 } };
7 if (finder.FindPath())
8 {
9 Console.WriteLine("Success!");
10 Console.WriteLine(finder.ResultString);
11 }
12 Console.ReadLine();
13 }

执行结果:

收获园豆:200
ChatinCode | 老鸟四级 |园豆:2272 | 2012-01-31 13:29

确实可行啊,感谢了。确实不错的方法。

WinStudy | 园豆:20 (初学一级) | 2012-01-31 17:25

@WinStudy: 这种算法问题,您应该先自己思考,如果有思路就应该自己做,要不然您在这方面不会有任何获益的。

ChatinCode | 园豆:2272 (老鸟四级) | 2012-02-01 08:49
其他回答(2)
0

首先处理一下矩阵,用9减去方框里非“-1”的数字。
如示例变为:
A 2 -1 8 3
1 7 9 4 5
7 6 0 -1 B

从A出发,设置每走一步,需要另加费用10000,-1为不可走据点。
到这里,问题就转变为:求从A出发到达B,费用最小的路径。
示例的费用为:
A + 10000 + 1 + 10000 + 7 + 10000 + 9 + 10000 + 4 +10000 + 5 + 10000 + B.即最小费用60026。

现在问题转为如何计算最小费用。
用一个数组记录每个据点当前寻找的最小费用,用来剪支。

朝九晚五 | 园豆:205 (菜鸟二级) | 2012-01-29 14:51

这里用9减之后再加上10000有什么意义呢,计算最小和计算最大有什么区别么?还有这里需要支持的地图最小要到1000x1000,也就是最大可能出现近一百万步,一万即使只累加五十万次结果还在int范围内么?另外用数组记录的方式来剪枝要怎么做呢?

支持(0) 反对(0) WinStudy | 园豆:20 (初学一级) | 2012-01-29 19:29
0

初始化,设置从A到每一个节点的最小费用为MinCost[i][j] = inf

按广度优先搜索的顺序访问每一个节点,并更新A到这个节点的未访问过邻居节点的MinCost

如果MinCost[MaxX][MaxY] = inf,没有通路; 否则,访问MinCost[MaxX][MaxY]

 

上面的流程,保证MinCost的路径是最短的:

每个节点的MinCost值更新都是由广度优先搜索的时候在它前面被访问的节点更新的.

KNi | 园豆:205 (菜鸟二级) | 2012-01-30 00:50

能不能给个例子,因为你的想法实际上和我的是一样的,但我在编程过程中发现这样行不通。能不能用上面的测试数据写个例子,只要能让前两种测试数据在100毫秒以内得到正确结果就行。

支持(0) 反对(0) WinStudy | 园豆:20 (初学一级) | 2012-01-30 01:47

@WinStudy: 原来描述有一点小问题,应该是MaxCost

你发现什么反例吗?

支持(0) 反对(0) KNi | 园豆:205 (菜鸟二级) | 2012-01-31 13:59
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册