算法竞赛入门经典(第2版)第4章 函数和递归
习题4-1象棋 UVa1589
感悟。
1、阅读书中题目,从网站下载英文原题,重点在看输出数据与格式。
2、老外没下过象棋,是很难短时间内弄懂题意的,感觉是一道出口转内销的问题。
3、读完题,第一直觉,要用二维数组。
4、对照书中中文,才明白has delivered a check意思是已经将军,还好看到这句,不然要花一定量的精力去处理是否将军。
5、基本思路是确定红方棋子管辖的范围,黑方棋子下一步移动的范围,若黑方范围为红方子集,则将死。
6、开始编写代码,突然想到黑方能将红方的棋子吃掉,这增加了编程的难度。
7、char name;scanf("%c",&name);无法读取单个字符,改成char c;char name[5];scanf("%s",name);c=name[0];成功。
8、红棋每个角色编写相关管辖范围,是本题的核心,考虑到黑棋能将红棋吃掉的情况,编写好程序,样例通过,提交WA,猜测格式问题。
9、用了一组牛叉的测试数据,找到了错误,编程的时候都想到了,但写的时候没写上,确实是出现在吃子上,修改提交AC。
10、对拍掌握得更熟练了,关键还要有测试数据,详见http://tieba.baidu.com/p/3680935088其中rand测试数据得编写。
11、如上所说,提供一组测试数据:
3 1 6
G 8 5
R 2 6
R 9 6
YES
12、看看日期2016-10-30,准备第二天研究10、提到得测试数据的写法。
13、看起来不可能完成任务,AC了,信心大增。数据是程序的核心。
14、如期研究了测试数据的编写,看懂了大概,但很多细节还有问题,慢慢研究。
AC代码如下,编译环境Dev-C++4.9.9.2
#include <stdio.h>
#include <string.h>
struct piece{
char name;
int row;
int col;
int live;
}red[10],redbak[10];
int board[13][13];//棋盘,只标记red棋子位置
int book[13][13];//标记red棋子管辖范围
int boardbak[13][13];//棋盘备份
void printinfo(){
int i,j;
for(i=1;i<=10;i++){
for(j=1;j<=9;j++){
printf("%d ",board[i][j]);
}
printf("\n");
}
printf("\n\n");
for(i=1;i<=10;i++){
for(j=1;j<=9;j++){
printf("%d ",book[i][j]);
}
printf("\n");
}
}
//处理管辖棋盘范围,比处理管辖皇宫范围要简单一些。
void general(int row,int col){//红棋general管辖范围
int i;
for(i=row-1;i>=1;i--){
if(board[i][col]==0){
if(i<=3&&i>=1)
book[i][col]=1;
}else//else跟随最近的if,这是个比较难找的错误
break;
}
}
void chariot(int row,int col){//管辖棋盘范围
int i;
for(i=row-1;i>=1;i--){//行处理,靠近上边界
if(board[i][col]==0)
book[i][col]=1;
else
break;
}
for(i=row+1;i<=10;i++){//行处理,靠近下边界
if(board[i][col]==0)
book[i][col]=1;
else
break;
}
for(i=col+1;i<=9;i++){//列处理,靠近右边界
if(board[row][i]==0)
book[row][i]=1;
else
break;
}
for(i=col-1;i>=1;i--){//列处理,靠近左边界
if(board[row][i]==0)
book[row][i]=1;
else
break;
}
}
void cannon(int row,int col){//炮管辖棋盘范围
int i;
int flag;
//架炮
flag=0;
for(i=row-1;i>=1;i--){//上方处理
if(flag){
if(board[i][col]==0)
book[i][col]=1;
else
break;
}
if(flag==0&&board[i][col]==1)
flag=1;
}
flag=0;
for(i=row+1;i<=10;i++){//下方处理
if(flag){
if(board[i][col]==0)
book[i][col]=1;
else
break;
}
if(flag==0&&board[i][col]==1)
flag=1;
}
flag=0;
for(i=col-1;i>=1;i--){//左方处理
if(flag){
if(board[row][i]==0)
book[row][i]=1;
else
break;
}
if(flag==0&&board[row][i]==1)
flag=1;
}
flag=0;
for(i=col+1;i<=9;i++){//右方处理
if(flag){
if(board[row][i]==0)
book[row][i]=1;
else
break;
}
if(flag=0&&board[row][i]==1)
flag=1;
}
}
void horse(int row,int col){//马管辖棋盘范围
//以管辖位置是否存在进行处理,越界问题就容易了
//竖线上有无马脚判断
if(row-2>=1&&col-1>=1){//左上角
if(board[row-1][col]==0)
book[row-2][col-1]=1;
}
if(row-2>=1&&col+1<=9){//右上角
if(board[row-1][col]==0)
book[row-2][col+1]=1;
}
if(row+2<=10&&col-1>=1){//左下角
if(board[row+1][col]==0)
book[row+2][col-1]=1;
}
if(row+2<=10&&col+1<=9){//右下角
if(board[row+1][col]==0)
book[row+2][col+1]=1;
}
//横线上有无马脚判断
if(row-1>=1&&col-2>=1){//左上角
if(board[row][col-1]==0)
book[row-1][col-2]=1;
}
if(row+1<=10&&col-2>=1){//左下角
if(board[row][col-1]==0)
book[row+1][col-2]=1;
}
if(row-1>=1&&col+2<=10){//右上角
if(board[row][col+1]==0)
book[row-1][col+2]=1;
}
if(row+1<=10&&col+2<=9){//右下角
if(board[row][col+1]==0)
book[row+1][col+2]=1;
}
}
int check(int newbr,int newbc,int n){//是否将军
int i;
for(i=0;i<n;i++)
if(red[i].row==newbr&&red[i].col==newbc){
red[i].live=0;
board[newbr][newbc]=0;//忘记清除棋盘被吃子的标识,忘记加上此句了。2016-10-30
}
for(i=0;i<n;i++)
if(red[i].live==1){
switch(red[i].name){
case 'G':
general(red[i].row,red[i].col);
break;
case 'R':
chariot(red[i].row,red[i].col);
break;
case 'H':
horse(red[i].row,red[i].col);
break;
case 'C':
cannon(red[i].row,red[i].col);
break;
}
}
if(book[newbr][newbc]==1)
return 1;
else
return 0;
}
int main(){
int n,br,bc;
char name[5];
int i;
int ans;
int res;
while(scanf("%d%d%d",&n,&br,&bc)==3&&n&&br&&bc){
ans=1;
memset(board,0,sizeof(board));
memset(red,0,sizeof(red));
for(i=0;i<n;i++){
scanf("%s%d%d",name,&(red[i].row),&(red[i].col));
red[i].name=name[0];
red[i].live=1;
board[red[i].row][red[i].col]=1;//标记棋盘落子位置
}
memcpy(boardbak,board,sizeof(board));
memcpy(redbak,red,sizeof(red));
memset(book,0,sizeof(book));
//black general moving like horse
if(br-1>=1){//上移处理
res=check(br-1,bc,n);
ans*=res;
// printf("上%d\n",res);
}
memcpy(board,boardbak,sizeof(boardbak));
memcpy(red,redbak,sizeof(redbak));
memset(book,0,sizeof(book));
if(br+1<=3){//下移处理
res=check(br+1,bc,n);
ans*=res;
// printf("下%d\n",res);
}
memcpy(board,boardbak,sizeof(boardbak));
memcpy(red,redbak,sizeof(redbak));
memset(book,0,sizeof(book));
if(bc-1>=4){//左移处理
res=check(br,bc-1,n);
ans*=res;
// printf("左%d\n",res);
}
memcpy(board,boardbak,sizeof(boardbak));
memcpy(red,redbak,sizeof(redbak));
memset(book,0,sizeof(book));
if(bc+1<=6){//右移处理
res=check(br,bc+1,n);//出现了笔误,将bc+1写成bc-1
ans*=res;
// printf("右%d\n",res);
}
if(ans==0)
printf("NO\n");
else
printf("YES\n");
}
return 0;
}
15、附上测试数据生成程序,来自http://tieba.baidu.com/p/3680935088,程序相关地方进行了本人的注释,但未完全看懂。
程序在Dev-C++4.9.9.2环境下运行通过。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<ctime>
using namespace std;
int main()
{
freopen("rand.in", "w", stdout);
srand(unsigned(time(0)));
int maxn = 6, count = 0;
int dir[11][10];
const char s0[] = "RRCCHH";
for (int s = (1<<maxn) - 1; s >= 1; s--)
for (int num = 1000; num; num--)
{
memset(dir, 0, sizeof(dir));
int n = 0, is = 0;
int r0 = 1 + rand() % 3, c0 = 4 + rand() % 3;//黑棋将的位置
dir[r0][c0] = 1;
int r1 = 8 + rand() % 3, c1 = 4 + rand() % 3;//红棋将的位置
while (c0 == c1)//目的是使黑红两将不在同一竖直位置
c1 = 4 + rand() % 3;
dir[r1][c1] = 1;
for (int i = 0; i < maxn; i++)//n值从6至1开始变化
if (s & (1<<i))
n++;
printf("\n%d %d %d\nG %d %d\n",n + 1, r0, c0, r1, c1);//打印第一行,第二行
count++;
for (int i = 0; i < maxn; i++)
if (s & (1<<i))
{
int r = 1 + rand() % 10, c = 1 + rand() % 9;//车马炮位置
if (is == 0 && s0[i] != 'C')
{
if (s0[i] != 'H')//车
{
r = r0 + 1;
c = c0;
//printf("车%c\n",s0[i]);
}
else//马
{
r = r0 + 2;
c = c0 + 1;
//printf("马%c\n",s0[i]);
}
is = 1;
}
else//炮车马
{
while (dir[r][c])
{
r = 1 + rand() % 10;
c = 1 + rand() % 9;
}
//printf("炮车马%c\n",s0[i]);
}
dir[r][c] = 1;
printf("%c %d %d\n", s0[i], r, c);
}
}
printf("0 0 0\n");
return 0;
}