Timus 1006. Square frames

Timus 1006. Square frames 要求根据屏幕上的图形给出一个能构成该图形的方框序列。

1006. Square frames

Time Limit: 2.0 second
Memory Limit: 16 MB
Frame consists of the following characters:
Problem illustration
N square frames (1 ≤ N ≤ 15) were sequentially drawn on screen 50 characters wide 20 lines tall. If parts of some frames intersect, only the part of the frame drawn latter remains visible. Each frame lies fully on the screen.
You need to write a program that builds a possible sequence of frames that (if drawn sequentially) would produce the same picture on the screen. Your sequence does not have to be the same with the original sequence used to build the picture on the screen. However, it should not contain more than 2000 frames.

Input

Problem illustration
The screen area was originally filled with dots (ASCII 46). Input contains the final picture on the screen after the sequence of frames is drawn.

Output

Your program should write to output the number of frames in the sequence built and the frames coordinates as follows:
K
X 1 Y 1 A 1

Xk Yk Ak
Here K is the number of frames, Xi and Yi are coordinates of the upper left frame corner (0 ≤ Xi ≤ 49, 0 ≤ Yi ≤ 19) and Ai is the length of the frame side (2 ≤ Ai). All numbers must be delimited with one or more spaces and/or line breaks.

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
Problem Source: USU Championship 1997

解答如下:

  1  using  System;
  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 方法是一样的。


返回目录

你可能感兴趣的:(frame)