这是2010年Google Code Jam里的一道问题,我将原题的大概意思翻译成中文了,附上自己的解决过程。
Problem:
在一个NXN的点阵中下棋,棋盘是直立的,所有棋子都会从上往下落,不会悬空在某一位置,棋子分为红色‘R’和蓝色‘B'两种,如下图,其中左边为正确的棋盘,右边为错误的棋盘,在右边的图中,棋子B应该要往下落一格:
棋盘是可以旋转的,每次旋转90度,可以逆时针旋转,也可以顺时针旋转,但是旋转后棋盘中的棋子之间的相对位置就会发生变化,如下图,旋转之后所有的棋子会由于重力的影响在棋盘中的相对位置都发生了变化:
下棋胜利的评判标准就是有连续K个相同棋子相连,这和五子棋的判别是一样的。关键是旋转的问题,因为旋转后棋子的位置会变。
约束条件:
1、只能旋转1次;
2、重力作用只在棋盘完全旋转90度之后才有效果;
3、如果需要旋转棋盘,必须完全旋转后,且棋子全部下落后才能进行局面评估。
问题输入的文件格式和输出格式如下图,输入格式中,文件的第一行是测试用例的个数,从第二行开始就是第一个用例的开始,第一个用例的第一行有两个数,第一个为7,代表是7X7的棋盘,第二个数3代表连续3个相连就算胜利。输出文件中,Neither表示红与黑都没有K个棋子相连,Red/Blue表示只有红色或者只有蓝色满足条件,Both表示都可以。
解决方法:
主要面对的问题就是旋转的次数只能是一次,如果不旋转又难以判定两种棋子是否符合条件。这个问题的解决办法就是无需旋转。因为可以发现,旋转后的棋子效果相当于将重力的方向改变90度即可,比如若将棋盘逆时针旋转90度达到的效果和将所有棋子推到左边所达到的效果是一样的。这样就完全不会用到旋转就可以判定两种棋子是否符合条件了。
向左移动棋子的代码:
for( int i = 0; i < N; i++ ) { int x = 0; for( int j = 0; j < N; j++ ) { if( piece[i][j] != '.' ) { piece[i][x] = piece[i][j]; x++; } } while( x<N ) { piece[i][x] = '.'; x++; } }
向右移动棋子的代码:
for( int i = 0; i < N; i++ ) { int x = N-1; for( int j = N-1; j >= 0; j-- ) { if( piece[i][j] != '.' ) { piece[i][x] = piece[i][j]; x--; } } while( x>=0 ) { piece[i][x] = '.'; x--; } }
局面评估的方法就是如果该点有棋子,就判断该棋子的8个方向,每个方向判断K步。代码如下:
//对有颜色的点的8个方向进行判断K步 /* 1 2 3 . . . 8 . R . 4 . . . 7 6 5 */ bool Test_8_Director( vector<vector<char> > piece, int i, int j, char color, int N, int K ) { int n = 1; //方向1 while( n <= K ) { //没达到K步就到边上了 if( (i-n)<0 || (j-n)<0 ) break; //没有连续K个相同 if( piece[i-n][j-n] != color ) break; n++; } if( n >= K ) return true; //方向2 n = 1; while( n <= K ) { //没达到K步就到边上了 if( (i-n)<0 ) break; //没有连续K个相同 if( piece[i-n][j] != color ) break; n++; } if( n >= K ) return true; //方向3 n = 1; while( n <= K ) { //没达到K步就到边上了 if( (i+n)>=N || (j+n)>=N ) break; //没有连续K个相同 if( piece[i+n][j+n] != color ) break; n++; } if( n >= K ) return true; //方向4 n = 1; while( n <= K ) { //没达到K步就到边上了 if( (j+n)>=N ) break; //没有连续K个相同 if( piece[i][j+n] != color ) break; n++; } if( n >= K ) return true; //方向5 n = 1; while( n <= K ) { //没达到K步就到边上了 if( (i+n)>=N || (j+n)>=N ) break; //没有连续K个相同 if( piece[i+n][j+n] != color ) break; n++; } if( n >= K ) return true; //方向6 n = 1; while( n <= K ) { //没达到K步就到边上了 if( (i+n)>=N ) break; //没有连续K个相同 if( piece[i+n][j] != color ) break; n++; } if( n >= K ) return true; //方向7 n = 1; while( n <= K ) { //没达到K步就到边上了 if( (i+n)>=N || (j-n)<0 ) break; //没有连续K个相同 if( piece[i+n][j-n] != color ) break; n++; } if( n >= K ) return true; //方向8 n = 1; while( n <= K ) { //没达到K步就到边上了 if( (j-n)<0 ) break; //没有连续K个相同 if( piece[i][j-n] != color ) break; n++; } if( n >= K ) return true; return false; }