习题4-3 黑白棋 UVa220

来源:互联网 发布:gui config.json下载 编辑:程序博客网 时间:2024/06/08 00:42
算法竞赛入门经典(第2版)第4章 函数和递归

题4-3黑白棋  UVa220

感悟。

1、阅读书中题目,从网站下载英文原题,重点在看输入输出数据与格式。

2、读题有三、四遍,一直被(2,3)困扰,再仔细看图(a),(3,7)位置已经放了一颗白棋,肯定合法的,

题目肯定有漏读的信息,对照(3,7)位置与黑棋关系,斜的,重新读题,重点搜索“斜”字,果然找到

“斜向”两字,至此,困扰解决。

3读一遍题目,发现了“夹住”的信息,编程之前,尽可能的读懂题目。

4、纯粹就只阅读了中文题目,就图(a)中几个非法的点,进行分析,若分析有不妥,读完英文题目,或编写成功程序后再作补充。一直在想图(a)中(3,6),(7,5)为何非法,一查英文原题,原来有8处合法,包括了(3,6),(7,5)。问题得以解决,在此处耗了不少时间。

5核心代码之一是判断棋子的合法位置,该代码的写法与快速排序代码有几分相似。其中部分思想又有些象习题4-1  象棋 UVa1589 马 的作用范围的写法。

6、现在学聪明了,字符的读取,先借助字符串做一个中转,又快又准。

7、有几个点要注意:
(1)、‘M’下棋轮换:After a move, the current player will
be changed to the player that did not move.
(2)、打印例子间空行:Put one blank line between
output from separate games and no blank lines anywhere else in the output.
(3)、处理'M'时,若无合法位置,则要改变当前落子颜色:If the current
player has no legal move, the current player is first changed to the other player and the move
will be the move of the new current player.

8、提交,竟然WA,怎么可能?
果断查询他人代码,http://blog.csdn.net/to_be_better/article/details/49961341找到 需要注意的地方,输出
黑白棋子数量的时候,数字要格式化输出占两位(%2d),天啊,这怎么可能想到?重读英文原题,确实有提到in the
format ‘Black - xx White - yy’,不过,现在才看明白这句的意思。提交WA。
继续查询他人代码,http://blog.csdn.net/kyoma/article/details/51824912找到 打印有效点的时候每个点之间都
有空格,但是最后一个点后面不能有,果断修改,提交WA。
9、没辙,找个AC代码,开始对拍。样例对拍,找不到差异。要找更多的数据,在
http://blog.csdn.net/h1120023921/article/details/52119686找到一些数据,开始对拍。竟然找不到相异处,还
需要更多的数据。
找不到更多数据,那么自个造吧,弄了个不合法的数据,对拍成功,但提交WA,看来测试数据全都是合法的,那么就想想有没特殊的情况没考虑到。
如下:M25是个不合法的数据(对黑棋、白棋都是)
1
--------
--------
--------
-BWWB---
-BWBW---
-WWBB---
-BBWW---
--------
W
L
M25
L
Q
10还想到一个办法,从一个棋局出发,开两个程序,进行模拟,直到棋盘下满。试试看
以题目中的第一个例子为例,开始对拍模拟下棋,手动输入,将整个棋盘下满,竟未找到问题。没办法了,准备开始读AC程序的源代码,看看能不能找到问题。找到http://blog.csdn.net/thudaliangrx/article/details/50700688程序,先提交AC,这才放心开始阅读,试着读一读,看看能不能找到思维的漏洞。
11、读到一处不同,先走棋子颜色,无需在循环内多次判断,只需要在循环外设置一次,接下来,程序会自动根据需要变换先走颜色,马上修改,提交WA。
12、采用替换的方法,将AC部分函数与本人函数进行替换,进行排错。
13、第一步,验证'Q'功能正确。
14、第二步,验证'L'功能正确。
15、第三步,验证'M'功能正确。
16、
else if(s[0]=='M'){//每次移动后,需要增加board数据,需要重设legal值
                row=s[1]-'0';
                col=s[2]-'0';
                setlegal(player);//每次移动前,总要先查找合法放置区域,该句漏了,整整查了两天11.3-11.4,终于AC了,想到都是泪
                if(legal[row][col]==1){//合法放置

错误如上,终于找到了,对应测试数据
1
--------
--------
---B--W-
---BBB--
--WBWW--
--BBBW--
--------
--------
W
L
M73
M52
L
Q
原来L,M是可以随机出现,而非按顺序出现。
17、上面代码是在替换过程中无意添加的。提交AC。

附上AC程序,编译环境Dev-C++4.9.9.2

#include <stdio.h>
#include <string.h>

const int maxn=10;
char board[maxn][maxn];//棋盘信息
int legal[maxn][maxn]; //落子合法位置
int direction[maxn];//八个方向,"米"字形,重左横开始,顺时针方向。
void printdirection(){
    int i;
    for(i=1;i<=8;i++)
        printf("%d ",direction[i]);
    printf("\n");
}
void printlist(){//打印可能位置
    int i,j;
    int count=0;
    
    for(i=1;i<=8;i++){
        for(j=1;j<=8;j++){
            if(legal[i][j]==1){
                count++;
                if(count==1)
                    printf("(%d,%d)",i,j);
                else//打印有效点的时候每个点之间都有空格,但是最后一个点后面不能有,果断修改,
                    printf(" (%d,%d)",i,j);
            }
        }
    }
    if(count==0)
        printf("No legal move.");
    printf("\n");
}
void list(char c,int row,int col){//枚举当前棋子的合法位置
    char other;
    int i,j;
    
    if(c=='B')//确定当前棋子,与对手棋子
        other='W';
    else
        other='B';
    //“米”字形,八个方位,左横、上竖、右横、下竖、左上斜、右上斜、左下斜、右下斜
    // 左横
    i=row;
    j=col;
    while(j-1>=1&&board[i][j-1]==other)//趟过other棋子,有快速排序代码的一点痕迹
        j--;
    if(j!=col&&j-1>=1&&board[i][j-1]=='-'){
        j--;
        legal[i][j]=1;
    }
    //右横
    i=row;
    j=col;
    while(j+1<=8&&board[i][j+1]==other)//趟过other棋子
        j++;
    if(j!=col&&j+1<=8&&board[i][j+1]=='-'){
        j++;
        legal[i][j]=1;
    }
    //上竖
    i=row;
    j=col;
    while(i-1>=1&&board[i-1][j]==other)//趟过other棋子
        i--;
    if(i!=row&&i-1>=1&&board[i-1][j]=='-'){
        i--;
        legal[i][j]=1;
    }
    //下竖
    i=row;
    j=col;
    while(i+1>=1&&board[i+1][j]==other)//趟过other棋子
        i++;
    if(i!=row&&i+1<=8&&board[i+1][j]=='-'){
        i++;
        legal[i][j]=1;
    }
    //左上斜
    i=row;
    j=col;
    while(i-1>=1&&j-1>=1&&board[i-1][j-1]==other){//趟过other棋子
        i--;
        j--;
    }
    if(i!=row&&j!=col&&i-1>=1&&j-1>=1&&board[i-1][j-1]=='-'){
        i--;
        j--;
        legal[i][j]=1;
    }
    //右下斜
    i=row;
    j=col;
    while(i+1<=8&&j+1<=8&&board[i+1][j+1]==other){//趟过other棋子
        i++;
        j++;
    }
    if(i!=row&&j!=col&&i+1<=8&&j+1<=8&&board[i+1][j+1]=='-'){
        i++;
        j++;
        legal[i][j]=1;
    }
    //右上斜
    i=row;
    j=col;
    while(i-1>=1&&j+1<=8&&board[i-1][j+1]==other){
        i--;
        j++;
    }
    if(i!=row&&j!=col&&i-1>=1&&j+1<=8&&board[i-1][j+1]=='-'){
        i--;
        j++;
        legal[i][j]=1;
    }
    //左下斜
    i=row;
    j=col;
    while(i+1<=8&&j-1>=1&&board[i+1][j-1]==other){
        i++;
        j--;
    }
    if(i!=row&&j!=col&&i+1<=8&&j-1>=1&&board[i+1][j-1]=='-'){
        i++;
        j--;
        legal[i][j]=1;
    }
}

void enumdirection(char c,int row,int col){//枚举枚举落子的作用范围
    char other;
    int i,j;
    memset(direction,0,sizeof(direction));//初始化方向
    if(c=='B')//确定当前棋子,与对手棋子
        other='W';
    else
        other='B';
    //“米”字形,八个方位,左横、上竖、右横、下竖、左上斜、右上斜、左下斜、右下斜
    // 左横 米 direction[1];
    i=row;
    j=col;
    while(j-1>=1&&board[i][j-1]==other)//趟过other棋子,有快速排序代码的一点痕迹
        j--;
    if(j!=col&&j-1>=1&&board[i][j-1]==c){
        j--;
        direction[1]=1;
    }
    //右横 米 dirction[5]
    i=row;
    j=col;
    while(j+1<=8&&board[i][j+1]==other)//趟过other棋子
        j++;
    if(j!=col&&j+1<=8&&board[i][j+1]==c){
        j++;
        direction[5]=1;
    }
    //上竖 米 dirction[3]
    i=row;
    j=col;
    while(i-1>=1&&board[i-1][j]==other)//趟过other棋子
        i--;
    if(i!=row&&i-1>=1&&board[i-1][j]==c){
        i--;
        direction[3]=1;
    }
    //下竖 米 dirction[7]
    i=row;
    j=col;
    while(i+1>=1&&board[i+1][j]==other)//趟过other棋子
        i++;
    if(i!=row&&i+1<=8&&board[i+1][j]==c){
        i++;
        direction[7]=1;
    }
    //左上斜 米 direction[2]
    i=row;
    j=col;
    while(i-1>=1&&j-1>=1&&board[i-1][j-1]==other){//趟过other棋子
        i--;
        j--;
    }
    if(i!=row&&j!=col&&i-1>=1&&j-1>=1&&board[i-1][j-1]==c){
        i--;
        j--;
        direction[2]=1;
    }
    //右下斜 米 direction[6]
    i=row;
    j=col;
    while(i+1<=8&&j+1<=8&&board[i+1][j+1]==other){//趟过other棋子
        i++;
        j++;
    }
    if(i!=row&&j!=col&&i+1<=8&&j+1<=8&&board[i+1][j+1]==c){
        i++;
        j++;
        direction[6]=1;
    }
    //右上斜 米 direction[4]
    i=row;
    j=col;
    while(i-1>=1&&j+1<=8&&board[i-1][j+1]==other){
        i--;
        j++;
    }
    if(i!=row&&j!=col&&i-1>=1&&j+1<=8&&board[i-1][j+1]==c){
        i--;
        j++;
        direction[4]=1;
    }
    //左下斜 米 direction[8]
    i=row;
    j=col;
    while(i+1<=8&&j-1>=1&&board[i+1][j-1]==other){
        i++;
        j--;
    }
    if(i!=row&&j!=col&&i+1<=8&&j-1>=1&&board[i+1][j-1]==c){
        i++;
        j--;
        direction[8]=1;
    }
}

void setboard(char c,int row,int col){//根据落子布局棋盘
    
    char other;
    int i,j;

    if(c=='B')//确定当前棋子,与对手棋子
        other='W';
    else
        other='B';
    
    board[row][col]=c;//安放落子
    //“米”字形,八个方位,左横、上竖、右横、下竖、左上斜、右上斜、左下斜、右下斜
    // 左横 米 direction[1];
    if(direction[1]==1){
        i=row;
        j=col;
        while(j-1>=1&&board[i][j-1]==other){//趟过other棋子,有快速排序代码的一点痕迹
            j--;
            board[i][j]=c;
        }
        if(j!=col&&j-1>=1&&board[i][j-1]==c){
            j--;
        }
    }
    
    //右横 米 dirction[5]
    if(direction[5]==1){
        i=row;
        j=col;
        while(j+1<=8&&board[i][j+1]==other){//趟过other棋子
            j++;
            board[i][j]=c;
        }
        if(j!=col&&j+1<=8&&board[i][j+1]==c){
            j++;
        }
    }
     
    //上竖 米 dirction[3]
    if(direction[3]==1){
        i=row;
        j=col;
        while(i-1>=1&&board[i-1][j]==other){//趟过other棋子
            i--;
            board[i][j]=c;
        }
        if(i!=row&&i-1>=1&&board[i-1][j]==c){
            i--;
        }
    }
    
    //下竖 米 dirction[7]
    if(direction[7]==1){
        i=row;
        j=col;
        while(i+1>=1&&board[i+1][j]==other){//趟过other棋子
            i++;
            board[i][j]=c;
        }
        if(i!=row&&i+1<=8&&board[i+1][j]==c){
            i++;
        }
    }
    
    //左上斜 米 direction[2]
    if(direction[2]==1){
        i=row;
        j=col;
        while(i-1>=1&&j-1>=1&&board[i-1][j-1]==other){//趟过other棋子
            i--;
            j--;
            board[i][j]=c;
        }
        if(i!=row&&j!=col&&i-1>=1&&j-1>=1&&board[i-1][j-1]==c){
            i--;
            j--;
        }
    }
    
    //右下斜 米 direction[6]
    if(direction[6]==1){
        i=row;
        j=col;
        while(i+1<=8&&j+1<=8&&board[i+1][j+1]==other){//趟过other棋子
            i++;
            j++;
            board[i][j]=c;
        }
        if(i!=row&&j!=col&&i+1<=8&&j+1<=8&&board[i+1][j+1]==c){
            i++;
            j++;
        }
    }
    
    //右上斜 米 direction[4]
    if(direction[4]==1){
        i=row;
        j=col;
        while(i-1>=1&&j+1<=8&&board[i-1][j+1]==other){
            i--;
            j++;
            board[i][j]=c;
        }
        if(i!=row&&j!=col&&i-1>=1&&j+1<=8&&board[i-1][j+1]==c){
            i--;
            j++;
        }
    }
    
    //左下斜 米 direction[8]
    if(direction[8]==1){
        i=row;
        j=col;
        while(i+1<=8&&j-1>=1&&board[i+1][j-1]==other){
            i++;
            j--;
            board[i][j]=c;
        }
        if(i!=row&&j!=col&&i+1<=8&&j-1>=1&&board[i+1][j-1]==c){
            i++;
            j--;
        }
    }
    
}

void countboard(){
    int i,j;
    int Bcount=0,Wcount=0;
    for(i=1;i<=8;i++){
        for(j=1;j<=8;j++){
            if(board[i][j]=='B')
                Bcount++;
            if(board[i][j]=='W')
                Wcount++;
        }
    }
    printf("Black - %2d White - %2d\n",Bcount,Wcount);
}
void setlegal(char player){//设置当前颜色棋子可行区域
    int i,j;
    memset(legal,0,sizeof(legal));//初始化合法区域,设置为0
    for(i=1;i<=8;i++){//扫描字符为player的棋子
        for(j=1;j<=8;j++){
            if(board[i][j]==player)
                list(player,i,j);
        }
    }
}

void printboard(){
    int i,j;
    for(i=1;i<=8;i++){
        for(j=1;j<=8;j++){
            printf("%c",board[i][j]);
        }
        printf("\n");
    }
}
int main(){
    int T;
    char line[10];
    int i,j;
    int row,col;
    int blank=0;
    char s[10];
    char player;
    scanf("%d",&T);
    while(T--){//事例次数
        if(blank>0)//打印例子间空行
            printf("\n");
        blank++;
        memset(board,0,sizeof(board));
        for(i=1;i<=8;i++){//读取棋盘信息
            scanf("%s",line);
            for(j=1;j<=8;j++){
                board[i][j]=line[j-1];
            }
        }
        scanf("%s",s);//读取当前落子颜色
        player=s[0];
        while(1){    
            scanf("%s",s);//读取当前棋盘操作
            if(s[0]=='L'){
                setlegal(player);
                printlist();//打印可能位置
            }else if(s[0]=='M'){//每次移动后,需要增加board数据,需要重设legal值
                row=s[1]-'0';
                col=s[2]-'0';
                setlegal(player);//每次移动前,总要先查找合法放置区域,该句漏了,整整查了两天11.3-11.4,终于AC了,想到都是泪
                if(legal[row][col]==1){//合法放置
                    enumdirection(player,row,col);//枚举可能放置方向
                    setboard(player,row,col);//根据落子,重新布局棋盘
                    countboard();//统计棋盘黑白棋个数
                }else{//非法放置,另一种颜色棋子先走,从题意看,另一种颜色棋子一定能走成功
                    player=player=='B'?'W':'B';//改变当前落子颜色,第一次用三目运算符
                    setlegal(player);
                    enumdirection(player,row,col);//枚举可能放置方向
                    setboard(player,row,col);//根据落子,重新布局棋盘
                    countboard();//统计棋盘黑白棋个数
                }
                //每次移动后,先手自动交换颜色
                player=player=='B'?'W':'B';
            }else if(s[0]=='Q'){//退出当前事例
                printboard();
                break;
            }
        }
    }
    return 0;
}





0 0
原创粉丝点击