1006. Square frames
Memory Limit: 16 MB
Input
Output
X1 Y1 A1
…
Xk Yk Ak
Sample
input | output |
---|---|
(see the figure above) |
6 16 11 7 32 14 4 4 8 8 11 6 7 36 11 3 28 8 3 |
解答如下:
2 using System.IO;
3 using System.Text;
4 using System.Collections.Generic;
5
6 namespace Skyiv.Ben.Timus
7 {
8 // http://acm.timus.ru/problem.aspx?space=1&num=1006
9 sealed class T1006
10 {
11 struct Frame
12 {
13 static readonly char Marked = '*';
14 static readonly char Background = '.';
15 static readonly char Vertical = (char)179;
16 static readonly char Horizontal = (char)196;
17 static readonly char LeftUpper = (char)218;
18 static readonly char RightUpper = (char)191;
19 static readonly char LeftBottom = (char)192;
20 static readonly char RightBottom = (char)217;
21
22 public static readonly char[] Uppers = new char[] { LeftUpper, RightUpper };
23 public static readonly char[] Lefts = new char[] { LeftUpper, LeftBottom };
24
25 int row, col, len;
26
27 Frame(int row, int col, int len)
28 {
29 this.row = row;
30 this.col = col;
31 this.len = len;
32 }
33
34 public static Frame GetValue(char c, int row, int col, int len)
35 {
36 if (c == LeftUpper) return new Frame(row, col, len);
37 if (c == RightUpper) return new Frame(row, col - len, len);
38 if (c == LeftBottom) return new Frame(row - len, col, len);
39 if (c == RightBottom) return new Frame(row - len, col - len, len);
40 throw new ArgumentOutOfRangeException("c");
41 }
42
43 public override string ToString()
44 {
45 return col + " " + row + " " + (len + 1);
46 }
47
48 public static bool IsCorner(char c)
49 {
50 return c == LeftUpper || c == RightUpper || c == LeftBottom || c == RightBottom;
51 }
52
53 public static bool IsHorizontal(char c)
54 {
55 return c == Horizontal;
56 }
57
58 public static bool IsVertical(char c)
59 {
60 return c == Vertical;
61 }
62
63 public static bool IsBackground(char c)
64 {
65 return c == Background;
66 }
67
68 public bool InScreen(char[,] screen)
69 {
70 return row >= 0 && row + len < screen.GetLength(0)
71 && col >= 0 && col + len < screen.GetLength(1);
72 }
73
74 public bool Search(Stack<Frame> stack, char[,] screen)
75 {
76 if (!IsFrame(screen)) return false;
77 Mark(screen);
78 stack.Push(this);
79 return true;
80 }
81
82 bool IsFrame(char[,] screen)
83 {
84 for (int i = 1; i < len; i++)
85 {
86 if (!IsBorderLine(Vertical, screen, i, 0)) return false;
87 if (!IsBorderLine(Vertical, screen, i, len)) return false;
88 if (!IsBorderLine(Horizontal, screen, 0, i)) return false;
89 if (!IsBorderLine(Horizontal, screen, len, i)) return false;
90 }
91 if (!IsBorderLine(LeftUpper, screen, 0, 0)) return false;
92 if (!IsBorderLine(RightUpper, screen, 0, len)) return false;
93 if (!IsBorderLine(LeftBottom, screen, len, 0)) return false;
94 if (!IsBorderLine(RightBottom, screen, len, len)) return false;
95 return true;
96 }
97
98 bool IsBorderLine(char c, char[,] screen, int dy, int dx)
99 {
100 char ch = screen[row + dy, col + dx];
101 return ch == Marked || ch == c;
102 }
103
104 void Mark(char[,] screen)
105 {
106 for (int i = 0; i <= len; i++)
107 screen[row + i, col] = screen[row + i, col + len] =
108 screen[row, col + i] = screen[row + len, col + i] = Marked;
109 }
110 }
111
112 static void Main()
113 {
114 using (TextReader reader = new StreamReader(
115 Console.OpenStandardInput(), Encoding.GetEncoding("iso-8859-1")))
116 {
117 new T1006().Run(reader, Console.Out);
118 }
119 }
120
121 void Run(TextReader reader, TextWriter writer)
122 {
123 char[,] screen = Read(reader);
124 Stack<Frame> stack = new Stack<Frame>();
125 SearchCorner(stack, screen);
126 SearchSide(stack, screen);
127 writer.WriteLine(stack.Count);
128 foreach (Frame frame in stack) writer.WriteLine(frame);
129 }
130
131 char[,] Read(TextReader reader)
132 {
133 List<string> list = new List<string>();
134 for (string s; (s = reader.ReadLine()) != null; ) list.Add(s);
135 char[,] v = new char[list.Count, list[0].Length];
136 for (int i = 0; i < list.Count; i++)
137 for (int j = 0; j < list[i].Length; j++)
138 v[i, j] = list[i][j];
139 return v;
140 }
141
142 void SearchCorner(Stack<Frame> stack, char[,] screen)
143 {
144 begin:
145 for (int i = 0; i < screen.GetLength(0); i++)
146 for (int j = 0; j < screen.GetLength(1); j++)
147 {
148 if (!Frame.IsCorner(screen[i, j])) continue;
149 for (int len = 1; ; len++)
150 {
151 Frame frame = Frame.GetValue(screen[i, j], i, j, len);
152 if (!frame.InScreen(screen)) break;
153 if (frame.Search(stack, screen)) goto begin;
154 }
155 }
156 }
157
158 void SearchSide(Stack<Frame> stack, char[,] screen)
159 {
160 begin:
161 for (int i = 0; i < screen.GetLength(0); i++)
162 for (int j = 0; j < screen.GetLength(1); j++)
163 if (SearchVertical(stack, screen, i, j)) goto begin;
164 else if (SearchHorizontal(stack, screen, i, j)) goto begin;
165 }
166
167 bool SearchVertical(Stack<Frame> stack, char[,] screen, int row, int col)
168 {
169 if (!Frame.IsVertical(screen[row, col])) return false;
170 for (int k = row - 1; k >= 0; k--)
171 {
172 if (Frame.IsBackground(screen[k, col]) || Frame.IsHorizontal(screen[k, col])) break;
173 foreach (char c in Frame.Uppers)
174 for (int len = row - k + 1; ; len++)
175 {
176 Frame frame = Frame.GetValue(c, k, col, len);
177 if (!frame.InScreen(screen)) break;
178 if (frame.Search(stack, screen)) return true;
179 }
180 }
181 return false;
182 }
183
184 bool SearchHorizontal(Stack<Frame> stack, char[,] screen, int row, int col)
185 {
186 if (!Frame.IsHorizontal(screen[row, col])) return false;
187 for (int k = col - 1; k >= 0; k--)
188 {
189 if (Frame.IsBackground(screen[row, k]) || Frame.IsVertical(screen[row, k])) break;
190 foreach (char c in Frame.Lefts)
191 for (int len = col - k + 1; ; len++)
192 {
193 Frame frame = Frame.GetValue(c, row, k, len);
194 if (!frame.InScreen(screen)) break;
195 if (frame.Search(stack, screen)) return true;
196 }
197 }
198 return false;
199 }
200 }
201 }
这道题目比较有意思,它在屏幕上给出了一张画有一些正方形方框的图形,这些方框可能互相覆盖,但不会超出屏幕的边界。现有要求你给出一个能构成该图形的方框序列。
本程序的入口点 Main 方法位于第 112 到 119 行。请注意第 114 到 115 行将输入流的编码设定为 iso-8859-1,这是因为这道题目使用最高位为 1 的 ASCII 码来表示方框线。如果使用缺省的 UTF-8 编码将无法正确读取题目的输入。
第 121 到 129 行的 Run 方法执行实际的工作。首先在第 123 行调用第 131 到 140 行的 Read 方法读取输入(请注意该方法可以读取任意大小的矩形屏幕的内容)。然后在第 124 行分配一个用来保存各个方框的堆栈 stack, 接着在第 125 到 126行依次调用 SearchCorner 和 SearchSide 方法进行搜索。最后在第 127 到 128 行输出保存在堆栈 stack 中的结果。
第 142 到 156 行的 SearchCorner 方法从方框的四个角开始在全屏幕进行搜索。第 148 行判断如果屏幕当前元素不是方框的四个角的话就跳过。第 149 到 154 行的循环依次从边长为 1 开始递增构造方框(第 151 行),直到方框超出屏幕为止(第 152 行)。第 153 行调用 Search 方法来进行搜索,如果成功地找到并标记一个方框,就跳回第 144 行重新开始搜索。注意这并不会造成死循环,因为找到的方框已经被标记过了,下次就不会再找这个方框了。这里使用了 goto 语句,因为这是很清楚自然的做法。如果要消除这个 goto 语句的话,势必要增加布尔变量形成复杂的控制流程,反而不如使用 goto 语句一目了然。
第 74 到 80 行的 Search 方法首先在第 76 行调用 IsFrame 方法判断能否构成一个正方形方框,如果可以的话,就在 77 行调用 Mark 方法标记该方框,然后将该方框压入堆栈(第 78 行)。
第 82 到 96 行的 IsFrame 方法首先在第 84 到 90 行判断构成方框的边线是否符合要求,然后在第 91 到 94 行判断构成方框的四个角是否符合要求。这是通过调用第 98 到 102 行的 IsBorderLine 方法进行判断的,如果该位置是标记过的也算符合要求,因为这表明这个位置原来是被其他方框覆盖了的。
第 104 到 109 行的 Mark 方法用来标记已经找到的方框,以防止下次搜索时再次找到同一方框陷入死循环。
第 158 到 165 行的 SearchSide 方法从方框的边线开始全屏幕搜索。在第 163 到 164 分别调用 SearchVertical 和 SearchHorizontal 来进行搜索,如果成功地找到并标记一个方框,就跳回第 160 行重新开始搜索。
第 167 到 182 行的 SearchVertical 方法在 169 行从方框的垂直线开始搜索。在 170 行开始从当前位置往上搜索直到屏幕顶部为止。如果碰到水平线或屏幕背景就停止搜索(第 172 行),因为这个方框肯定是被其它方框覆盖的,它不能在这次搜索中压入堆栈,必须留待以后处理。然后在第 173 行开始使用两个顶角试图构造方框(第 176 行),其边长从 row - k + 1 开始依次增大(第 174 行),直到方框超出屏幕为止(第 177 行)。第 178 行调用 Search 方法来进行搜索。
第 184 到 199 行到 SearchHorizontal 方法从方框的水平线开始搜索。它的工作原理和 SearchVertical 方法是一样的。
返回目录