1016. A Cube on the Walk
Memory Limit: 16 MB
Input
Output
Sample
input | output |
---|---|
e2 e3 0 8 1 2 1 1 |
5 e2 d2 d1 e1 e2 e3 |
这道题目是说,一个立方体放在国际象棋棋盘的一个格子上,该立方体每一面都和棋盘的格子一样大。该立方体每一面都标有一个非负整数。你可以在棋盘上滚动该立方体,在这期间计算立方体底面数字的和。你的任务是找出一条从给定的起点到终点的路径,使得前面所说的和最小。
输入只有一行,使用空格作为分隔符。首先是棋盘上的起点和终点的坐标,然后是立方体上的六个整数,按顺序为:前、后、顶、右、底、左面。举例如下: e2 e3 0 8 1 2 1 1 。
输出也只有一行,使用空格作为分隔符。首先是所求的最小的和。然后依次给出所求的路径的坐标。举例如下:5 e2 d2 d1 e1 e2 e3 。这个路径共六步,其长度为:1 + 1 + 0 + 1 + 1 + 1 = 5 。
上面的输入和输出的例子是题目中给出的。其实同一个输入可能不止一条最短路径。例如使用我的程序得到的输出如下:5 e2 e1 f1 f2 e2 e3 。这个路径也是六步,其长度为: 1 + 0 + 2 + 1 + 0 + 1 = 5 。
好了,我们还是来看程序吧。
第 43 到 116 行的 Cube 类代表立方体。
第 45 到 47 行的 X、Y、Z 表示立方体绕三个坐标轴旋转,如图所示。
第 48 行的 DCube 数组的四个元素表示立方体在棋盘上向“左、上、右、下”滚动。与此相对应的第 53 行的 DSize 数组的四个元素表示与棋盘当前格子相邻的“左、上、右、下”格子。
第 49 行的 Levels 数组共有 24 个元素,表示同一立方体的 24 种不同的状态,是通过调用第 106 到 115 行的 GetLevels 方法得到的。类似地,“Timus 1015. Test the Difference! 要求将赌场中的骰子分类” 中每种骰子也对应 24 种不同的状态。
程序的主体是第 123 到 130 行的 Run 方法。该方法的第 126 行调用 Read 方法获取输入。然后在第 127 行调用 Dijkstra 方法寻找最短路径。最后在第 128 到 129 行输出结果。
第 142 到 164 行的 Dijkstra 方法是程序的关键部分。第 144 行的三维数组 prev 用来记录立方体的路径,由 24 层的 8x8 棋盘构成。第 145 行的优先队列 q 的元素类型是 Rectangle,其构造函数的参数是 new Comparer() 。Comparer 类在第 11 到 17 行定义,用于逆序比较 Rectangle 的 Width 属性,该 Width 属性表示所求的最小的和。第 146 行将立方体的初始位置压入优先队列。第 147 行的三维数组 dist 大小是 8x8x24,用来记录路径长度,其元素被初始化为无穷大。第 150 到 162 行的循环使用 Dijkstra 算法寻找最短路径。最后在第 163 行调用 GetPath 方法返回找到的最短路径。
2 using System.IO;
3 using System.Drawing;
4 using System.Collections.Generic;
5
6 namespace Skyiv.Ben.Timus
7 {
8 // http://acm.timus.ru/problem.aspx?space=1&num=1016
9 sealed class T1016
10 {
11 sealed class Comparer : IComparer<Rectangle>
12 {
13 public int Compare(Rectangle x, Rectangle y)
14 {
15 return (x.Width == y.Width) ? 0 : ((x.Width < y.Width) ? 1 : -1);
16 }
17 }
18
19 sealed class PriorityQueue<T>
20 {
21 List<T> queue = new List<T>();
22 IComparer<T> comparer;
23
24 public PriorityQueue(IComparer<T> comparer)
25 {
26 this.comparer = comparer;
27 }
28
29 public void Push(T v)
30 {
31 int i = queue.BinarySearch(v, comparer);
32 queue.Insert((i < 0) ? ~i : i, v);
33 }
34
35 public T Pop()
36 {
37 T v = queue[queue.Count - 1];
38 queue.RemoveAt(queue.Count - 1);
39 return v;
40 }
41 }
42
43 sealed class Cube : IEquatable<Cube>, IComparable<Cube>
44 {
45 static readonly Cube X = new Cube(0, 1, 5, 2, 3, 4);
46 static readonly Cube Y = new Cube(2, 4, 1, 3, 0, 5);
47 static readonly Cube Z = new Cube(3, 5, 2, 1, 4, 0);
48 static readonly Cube[] DCube = { X, Y, X * X * X, Y * Y * Y };
49 static readonly Cube[] Levels = GetLevels();
50 static readonly int idxBottom = 4;
51 public static readonly int Size = 8; // for chessboard: 8x8
52 public static readonly int Level = Levels.Length; // 24
53 public static readonly Size[] DSize = { new Size(-1,0), new Size(0,1), new Size(1,0), new Size(0,-1) };
54
55 int[] v = new int[6]; // Forward, Backward, Top, Right, Bottom, Left
56 public int Bottom { get { return v[idxBottom]; } }
57
58 Cube()
59 {
60 for (int i = 0; i < v.Length; i++) v[i] = i;
61 }
62
63 public Cube(params int[] v)
64 {
65 for (int i = 0; i < v.Length; i++) this.v[i] = v[i];
66 }
67
68 public bool Equals(Cube other)
69 {
70 return CompareTo(other) == 0;
71 }
72
73 public int CompareTo(Cube other)
74 {
75 int i = 0;
76 while (i < v.Length && v[i] == other.v[i]) i++;
77 return (i == v.Length) ? 0 : ((v[i] < other.v[i]) ? -1 : 1);
78 }
79
80 public static Cube operator*(Cube x, Cube y)
81 {
82 Cube z = new Cube();
83 for (int i = 0; i < z.v.Length; i++) z.v[y.v[i]] = x.v[i];
84 return z;
85 }
86
87 public static Cube operator/(Cube x, Cube y)
88 {
89 Cube z = new Cube();
90 for (int i = 0; i < z.v.Length; i++) z.v[i] = y.v[x.v[i]];
91 return z;
92 }
93
94 public static int GetLevel(int level, int n)
95 {
96 return Array.BinarySearch(Levels, Levels[level] / DCube[n]);
97 }
98
99 public int GetBottom(int level)
100 {
101 int i = 0;
102 while (Levels[level].v[i] != idxBottom) i++;
103 return v[i];
104 }
105
106 static Cube[] GetLevels()
107 {
108 List<Cube> list = new List<Cube>();
109 Cube v, x = new Cube(), y = new Cube(), z = new Cube();
110 for (int i = 0; i < 4; i++, x *= X)
111 for (int j = 0; j < 4; j++, y *= Y)
112 for (int n, k = 0; k < 4; k++, z *= Z)
113 if ((n = list.BinarySearch(v = x * y * z)) < 0) list.Insert(~n, v);
114 return list.ToArray();
115 }
116 }
117
118 static void Main()
119 {
120 new T1016().Run(Console.In, Console.Out);
121 }
122
123 void Run(TextReader reader, TextWriter writer)
124 {
125 Point start, end;
126 Cube cube = Read(reader, out start, out end);
127 Point[] path = Dijkstra(cube, start, end);
128 writer.Write(path[0].X);
129 for (int i = 1; i < path.Length; i++) writer.Write(PutPosition(path[i]));
130 }
131
132 Cube Read(TextReader reader, out Point start, out Point end)
133 {
134 string[] ss = reader.ReadLine().Split();
135 start = GetPosition(ss[0]);
136 end = GetPosition(ss[1]);
137 int[] v = new int[ss.Length - 2];
138 for (int i = 0; i < v.Length; i++) v[i] = int.Parse(ss[i + 2]);
139 return new Cube(v);
140 }
141
142 Point[] Dijkstra(Cube cube, Point start, Point end)
143 {
144 Rectangle[,,] prev = new Rectangle[Cube.Size, Cube.Size, Cube.Level];
145 PriorityQueue<Rectangle> q = new PriorityQueue<Rectangle>(new Comparer());
146 q.Push(new Rectangle(start.X, start.Y, cube.Bottom, 0));
147 int[,,] dist = GetDistance();
148 dist[start.X, start.Y, 0] = cube.Bottom;
149 Rectangle v;
150 while ((v = q.Pop()).Location != end)
151 if (v.Width <= dist[v.X, v.Y, v.Height])
152 for (int i = 0; i < Cube.DSize.Length; i++)
153 {
154 Point pt = v.Location + Cube.DSize[i];
155 if (pt.X < 0 || pt.Y < 0 || pt.X >= Cube.Size || pt.Y >= Cube.Size) continue;
156 int height = Cube.GetLevel(v.Height, i);
157 int width = v.Width + cube.GetBottom(height);
158 if (dist[pt.X, pt.Y, height] <= width) continue;
159 dist[pt.X, pt.Y, height] = width;
160 prev[pt.X, pt.Y, height] = v;
161 q.Push(new Rectangle(pt.X, pt.Y, width, height));
162 }
163 return GetPath(prev, start, end, v.Height, dist[end.X, end.Y, v.Height]);
164 }
165
166 Point[] GetPath(Rectangle[,,] prev, Point start, Point end, int id, int len)
167 {
168 Stack<Point> path = new Stack<Point>();
169 for (Rectangle v = new Rectangle(end.X, end.Y, 0, id);
170 v.Location != start || v.Height != 0;
171 v = prev[v.X, v.Y, v.Height]) path.Push(v.Location);
172 path.Push(start);
173 path.Push(new Point(len, 0));
174 return path.ToArray();
175 }
176
177 int[,,] GetDistance()
178 {
179 int[,,] dist = new int[Cube.Size, Cube.Size, Cube.Level];
180 for (int i = 0; i < dist.GetLength(0); i++)
181 for (int j = 0; j < dist.GetLength(1); j++)
182 for (int k = 0; k < dist.GetLength(2); k++)
183 dist[i, j, k] = int.MaxValue; // infinity
184 return dist;
185 }
186
187 Point GetPosition(string s)
188 {
189 return new Point(s[0] - 'a', s[1] - '1');
190 }
191
192 string PutPosition(Point pt)
193 {
194 return string.Format(" {0}{1}", (char)(pt.X + 'a'), pt.Y + 1);
195 }
196 }
197 }
返回目录