第四章 函数和递归(习题篇)
习题4-1 象棋(Xiangqi,UVa1589)
考虑一个象棋残局,其中红方有n(2<=n<=7)个棋子,黑方只有一个将。红方除了有一个帅(G)之外还有三种可能的棋子:车(R),马(H),炮(C),并且需要考虑“蹩马脚”与将和帅不能照面(将、帅如果在一条直线上,中间又不隔着任何棋子的情况下,将要走子的一方胜)的规则。
输入所有棋子的位置,保证局面合法并且红方已经将军。你的任务是判断红方是否已经把黑方将死。
#include <stdio.h>
/*棋盘上红方老家是在楚河下方,黑方老家在楚河上方;
用原点在棋盘左下,x轴向右增加,y轴向上增加的坐标表示;
棋子可以摆放的点x的取值范围为1,2,...,9,y的取值范围为1,2,...,10。*/
#define xMax 9
#define yMax 10
enum cheese { G = 0, R1, R2, H1, H2, C1, C2 };//G是帅,R1是车1,H1是马1,C1是炮1
char *name[7] = { "帅","车1","车2","马1","马2","炮1","炮2" };
/*马可以动八个方向,Horse[0][0/1]表示马在0号方向移动x/y方向的增量,
Horse[0][2/3]表示在0方向撇脚位的x/y方向的增量。*/
int Horse[8][4] = { { -1,2,0,1 },{ 1,2,0,1 },{ 2,1,1,0 },{ 2,-1,1,0 },
{ 1,-2,0,-1 },{ -1,-2,0,-1 },{ -2,-1,-1,0 },{ -2,1,-1,0 } };
/*黑棋的将可以动四个方向。将x的取值范围为4,5,6,y的取值范围为8,9,10*/
int Admiral[4][2] = { { -1,0 } ,{ 0,1 } ,{ 1,0 } ,{ 0,-1 } };
struct Node//存储棋子位置的数据结构
{
int x, y;
};
Node Nodes[7];//对应name[7]个棋子,请将棋盘上没有的棋子相应的x,y置0.
bool Vs(cheese A, int x, int y)//红旗能吃掉在位置x,y上的黑棋吗?
{
if (Nodes[A].x == x&&Nodes[A].y == y)
return false;
switch (A)
{
case cheese::G:
if (Nodes[A].x == x)
{
for (int i = 1;i < 7;i++)//i从1开始不要判断自己,判断也可以
{
if (Nodes[i].x == x && (Nodes[A].y<Nodes[i].y&&Nodes[i].y<y) || (Nodes[A].y>Nodes[i].y&&Nodes[i].y>y))
{//要是帅和目标棋子中间有棋子则不能吃掉目标棋子
return false;
}
}
return true;
}
return false;
break;
case cheese::R1:
case cheese::R2:
if (Nodes[A].x == x)
{
for (int i = 0;i < 7;i++)
{
if (Nodes[i].x == x && (Nodes[A].y<Nodes[i].y&&Nodes[i].y<y) || (Nodes[A].y>Nodes[i].y&&Nodes[i].y>y))
{//要是车和目标棋子中间有棋子则不能吃掉目标棋子
return false;
}
}
return true;
}
else if (Nodes[A].y == y)
{
for (int i = 0;i < 7;i++)
{
if (Nodes[i].y == y && (Nodes[A].x<Nodes[i].x&&Nodes[i].x<x) || (Nodes[A].x>Nodes[i].x&&Nodes[i].x>x))
{//要是车和目标棋子中间有棋子则不能吃掉目标棋子
return false;
}
}
return true;
}
else
{
return false;
}
break;
case cheese::H1:
case cheese::H2:
for (int i = 0;i < 8;i++)
{
int posX = Nodes[A].x + Horse[i][0];
int posY = Nodes[A].y + Horse[i][1];
if ((0 < posX &&posX <= xMax) && (0 < posY&&posY <= yMax))
{//判断移动是否会超出棋盘
if (x == posX&&y == posY)
{
int iOk = true;
for (int j = 0;j < 7;j++)
{
if ((Nodes[A].x + Horse[i][2]) == Nodes[j].x && (Nodes[A].y + Horse[i][3]) == Nodes[j].y)
{//如果马能吃掉目标还有判断是否撇脚
iOk = false;
break;
}
}
if (iOk)
{
return true;
}
}
}
}
return false;
break;
case cheese::C1:
case cheese::C2:
if (Nodes[A].x == x)
{
int num = 0;
for (int i = 1;i < 7;i++)
{
if ((Nodes[i].x == x) && (Nodes[A].y<Nodes[i].y&&Nodes[i].y<y) || (Nodes[A].y>Nodes[i].y&&Nodes[i].y>y))
{//要是炮和目标棋子中间有棋子且只有一个则能吃掉目标棋子
num++;
}
}
if (num == 1)
return true;
else
return false;
}
else if (Nodes[A].y == y)
{
int num = 0;
for (int i = 1;i < 7;i++)
{
if (Nodes[i].y == y && (Nodes[A].x<Nodes[i].x&&Nodes[i].x<x) || (Nodes[A].x>Nodes[i].x&&Nodes[i].x>x))
{//要是车和目标棋子中间有棋子则不能吃掉目标棋子
num++;
}
}
if (num == 1)
return true;
else
return false;
}
else
{
return false;
}
break;
default:
return false;
}
}
int main()
{
/*freopen("input.txt", "rb", stdin);*/
for (int i = 0;i < 7;i++)
{
printf("输入红方%s的位置,没有%s则输入0 0\n", name[i], name[i]);
if (scanf("%d%d", &Nodes[i].x, &Nodes[i].y) != 2)
{
Nodes[i].x = Nodes[i].y = 0;
}
}
int Admiral_X, Admiral_Y;
printf("输入黑方将军的位置\n");
scanf("%d%d", &Admiral_X, &Admiral_Y);
//先判断红方是否要将军
bool isCanWill = false;
for (int i = 0;i < 7;i++)
{
if (Nodes[i].x != 0)
{
if (Vs((cheese)i, Admiral_X, Admiral_Y))
{
isCanWill = true;
break;
}
}
}
if (isCanWill)
{
for (int i = 0;i < 4;i++)
{
int posX = Admiral_X + Admiral[i][0];
int posY = Admiral_Y + Admiral[i][1];
if (3 < posX&&posX < 7 && 7 < posY&&posY < 11)
{
bool canNotEat = true;
for (int j = 0;j < 7;j++)
{
if (Nodes[j].x != 0)
{
if (Vs((cheese)j, posX, posY))
{
canNotEat = false;
break;
}
}
}
if (canNotEat)
{
printf("没有将死!\n");
return 0;
}
}
}
printf("将死!\n");
}
else
{
printf("请保证红发已经将军!\n");
}
return 0;
}
习题4-2:正方形(Squares,UVa201)
n(2<=n<=9)个点组成的正方体,其中有m条边,H i j表示边(i,j)~(i,j+1),V j i表示边(i,j)~(i+1,j),问不同边长的子正方体各有多少个。
思路:遍历所有可能边长为1,2,3…k的正方形,判断是否存在足够多的边能构成长度为k的正方形,能则计数。
#include <stdio.h>
#define maxn 9
int n;
int H[maxn + 1][maxn];
int V[maxn][maxn + 1];
bool isSquare(int x, int y, int length)
{
for (int z = 0;z < length;z++)
{//上边
if (!H[x][y + z] || !V[x + z][y + length] || !H[x + length][y + z] || !V[x + z][y])
{
return false;
}
}
return true;
}
int Squares(int length)
{
int count = 0;
for (int i = 1;i <= n - length;i++)
{
for (int j = 1;j <= n - length;j++)
{
if (isSquare(i, j, length))
{
count++;
}
}
}
return count;
}
int main()
{
/*freopen("input.txt", "rb", stdin);*/
printf("输入点阵数:\n");
scanf("%d", &n);
if (2 > n&&n > 9)
{
printf("点阵数的范围要是2-9.\n");
return 0;
}
printf("输入%d*%d横边矩阵,有边输入1,无边输入0.\n", n, n - 1);
for (int i = 1;i <= n;i++)
{
for (int j = 1;j <= n - 1;j++)
{
scanf("%d", &H[i][j]);
}
}
printf("输入%d*%d纵边矩阵,有边输入1,无边输入0.\n", n - 1, n);
for (int i = 1;i <= n - 1;i++)
{
for (int j = 1;j <= n;j++)
{
scanf("%d", &V[i][j]);
}
}
for (int i = 1;i < n;i++)
{
printf("边长为%d的正方形的数量:%d\n", i, Squares(i));
}
return 0;
}
习题4-3:黑白棋(Othello,UVa220)
#include <stdio.h>
#define maxn 9
char map[maxn][maxn];
char cmd[12];
int mark[8][2] = { {-1,-1} ,{-1,0}, {-1,1}, {0,1}, {1,1}, {1,0}, {1,-1}, {0,-1} };
char *name[2] = { "白棋","黑棋" };
int cheese = 0;//0表示白棋,1表示黑棋
bool isInMapRan(int x, int y)
{
if (0 < x&&x < 9 && y>0 && y < 9)
return true;
return false;
}
bool isLegalPos(int cheese, int x, int y)
{
char pos = 'W';
char neg = 'B';
if (cheese)
{
pos = 'B';
neg = 'W';
}
if (map[x][y] == '-')
{
for (int i = 0;i < 8;i++)
{
int posX, posY;
posX = x + mark[i][0];
posY = y + mark[i][1];
while (isInMapRan(posX, posY)&&map[posX][posY] == neg)
{
posX += mark[i][0];
posY += mark[i][1];
}
if (!isInMapRan(posX, posY))
break;
if (map[posX][posY] == pos && (posX != x + mark[i][0] || posY != y + mark[i][1]))
{
return true;
}
}
}
return false;
}
int list(int cheese,bool enable_put=true)
{
int count = 0;
for (int i = 1;i < 9;i++)
{
for (int j = 1;j < 9;j++)
{
if (isLegalPos(cheese, i, j))
{
count++;
if (enable_put)
{
printf("(%d,%d) ", i, j);
}
}
}
}
if (enable_put)
{
printf("\n");
}
return count;
}
void countCheese(int *w, int *b)
{
*w = *b = 0;
for (int i = 1;i < 9;i++)
{
for (int j = 1;j < 9;j++)
{
if (map[i][j]=='B')
{
(*b)++;
}
if(map[i][j] == 'W')
{
(*w)++;
}
}
}
}
void DelCheese(int cheese, int x, int y)
{
char pos = 'W';
char neg = 'B';
if (cheese)
{
pos = 'B';
neg = 'W';
}
for (int i = 0;i < 8;i++)
{
int posX, posY;
posX = x + mark[i][0];
posY = y + mark[i][1];
while (isInMapRan(posX, posY) && map[posX][posY] == neg)
{
posX += mark[i][0];
posY += mark[i][1];
}
if (!isInMapRan(posX, posY))
break;
if (map[posX][posY] == pos && (posX != x + mark[i][0] || posY != y + mark[i][1]))
{
posX = posX - mark[i][0];
posY = posY - mark[i][1];
while (map[posX][posY] != pos)
{
map[posX][posY] = cheese ? 'B' : 'W';
posX = posX - mark[i][0];
posY = posY - mark[i][1];
}
}
}
}
void put()
{
int a;
scanf("%d", &a);
int x, y;
x = a / 10;
y = a % 10;
a = list(cheese, false);
if (a == 0)
{
countCheese(&x, &y);
printf("黑棋还剩%d,白棋还剩%d.\n", y, x);
printf("%s已经没有合法的位置了,切换至%s!清再输入指令!\n", name[cheese], name[(cheese + 1) % 2]);
cheese = (cheese + 1) % 2;
return;
}
if (!isLegalPos(cheese, x, y))
{
printf("%d,%d不是%s合法的位置,请再输入正确的指令!\n", x, y, name[cheese]);
countCheese(&x, &y);
printf("黑棋还剩%d,白棋还剩%d.\n", y, x);
return;
}
map[x][y] = cheese ? 'B' : 'W';
DelCheese(cheese, x, y);
countCheese(&y, &x);
printf("黑棋还剩%d,白棋还剩%d.\n", x, y);
}
void showMap()
{
for (int i = 1;i < 9;i++)
{
for (int j = 1;j < 9;j++)
{
printf("%2c", map[i][j]);
}
printf("\n");
}
}
int main()
{
freopen("input.txt", "r", stdin);
printf("输入初始8*8的棋盘(空位、黑棋、白起分别用“-”、“B”、“W”表示):\n");
for (int i = 1;i < 9;i++)
{
scanf("%s", cmd);
for (int j = 1;j < 9;j++)
{
map[i][j] = cmd[j - 1];
}
}
printf("请输入指令\n");
freopen("CON", "r", stdin);
while (scanf("%s", cmd))
{
if (cmd[0] == 'L') list(cheese);
if (cmd[0] == 'M') put();
if (cmd[0] == 'Q')
{
showMap();
break;
}
printf("请输入指令\n");
}
return 0;
}
习题4-4:骰子涂色(Cube painting,UVa253)
输入:
rbrggbrgrgbb
输出:
FALSE
#include <stdio.h>
#include<string.h>
#define maxn 20
char m[maxn];
int turn[6][6] = { { 0,1,2,3,4,5 },{ 1,5,2,3,0,4 },{ 2,1,5,0,4,3 },{ 3,1,0,5,4,2 },{ 4,0,2,3,5,1 },{ 5,4,2,3,1,0 } };
int main()
{
freopen("input.txt", "r", stdin);
char A[7], B[7], C[7];
while (scanf("%s", m) == 1)
{
for (int i = 0;i < 6;i++)
{
A[i] = m[i];
B[i] = m[i + 6];
}
A[6] = B[6] = C[6] = '\0';
bool isFind = false;
for (int i = 0;i < 6;i++)
{
if (A[0] == B[i])
{
for (int j = 0;j < 6;j++)
{
C[j] = B[turn[i][j]];
}
if (strcmp(A, C) == 0)
{
printf("TRUE\n");
isFind = true;
break;
}
else
{
for (int z = 0;z < 3;z++)
{
char ch1, ch2, ch3, ch4;
ch1 = C[2];
ch2 = C[4];
ch3 = C[1];
ch4 = C[3];
C[1] = ch1;
C[2] = ch2;
C[3] = ch3;
C[4] = ch4;
if (strcmp(A, C) == 0)
{
printf("TRUE\n");
isFind = true;
break;
}
}
if (isFind)
break;
}
}
}
if (!isFind)
{
printf("FALSE\n");
}
}
return 0;
}
习题4-5:IP网络(IP Networks, UVa1590)
题目大意:给出一个IP地址集合,求出其对应的最小子网的网络号和掩码。
思路一:把IP地址化成32位二进制,从IP左到右比较所有的IP找到他们的公共前缀的位置。把这个位置后的二进制置零就是网络号;把这位置前以及这个位置的二进制全写为一,这个位置之后的置零则为子网掩码。·
输入:
3
194.85.160.177
194.85.160.183
194.85.160.178
0
输出:
194.85.160.176
255.255.255.248
#include <stdio.h>
#include<string.h>
int count[32];
int main()
{
freopen("input.txt", "r", stdin);
int n;
int part[4];
int res1[4];
int res2[4];
while (scanf("%d", &n) == 1 && n)
{
memset(count, 0, sizeof(count));
for (int i = 0;i < n;i++)
{
scanf("%d.%d.%d.%d", &part[0], &part[1], &part[2], &part[3]);
for (int j = 0;j < 8;j++)
{
count[j] += (part[0] >> (7 - j)) & 0x1;
count[j + 8] += (part[1] >> (7 - j)) & 0x1;
count[j + 16] += (part[2] >> (7 - j)) & 0x1;
count[j + 24] += (part[3] >> (7 - j)) & 0x1;
}
}
printf("\n");
int num;
for (int i = 0;i < 32;i++)
{
num = i;
if (count[i] % n != 0)
{
break;
}
}
if (num == 0)
{
printf("这%d不能构成一个子网络\n", n);
return 0;
}
for (int i = 0;i < 4;i++)
{
if (i < num / 8)
{
res1[i] = part[i];
res2[i] = 255;
}
else if (i == num / 8)
{
int tmp = part[num / 8];
res1[i] = 0;
res2[i] = 0;
for (int z = 0;z < num % 8;z++)
{
res1[i] += ((tmp >> (7 - z)) & 0x1) << (7 - z);
res2[i] += (0x1) << (7 - z);
}
}
else
{
res1[i] = 0;
res2[i] = 0;
}
}
printf("%d", res1[0]);
printf(".%d", res1[1]);
printf(".%d", res1[2]);
printf(".%d\n", res1[3]);
printf("%d", res2[0]);
printf(".%d", res2[1]);
printf(".%d", res2[2]);
printf(".%d\n", res2[3]);
}
return 0;
}
思路二:考察二进制与十进制之间的转换。最小网络地址为最长公共前缀加上后面全部补零;子网掩码则为最长公共前缀对应的位全部置1,然后后面全部补零。
#include <stdio.h>
#include<string.h>
int count[32];
int main()
{
freopen("input.txt", "r", stdin);
int n;
while (scanf("%d", &n) == 1 && n)
{
int part[4];
memset(count, 0, sizeof(count));
for (int i = 0;i < n;i++)
{
scanf("%d.%d.%d.%d", &part[0], &part[1], &part[2], &part[3]);
for (int j = 0;j < 4;j++)
{
for (int k = 0;k < 8;k++)
{
count[j * 8 + k] += ((part[j] >> (7 - k)) & 0x1) ? 1 : 0;
}
}
}
int c = 0;
for (c = 0;c < 32;c++)
{
if (count[c] != 0 && count[c] != n)
break;
}
memset(part, 0, sizeof(part));
for (int i = 0;i < 4;i++)
{
for (int j = 0;j < 8;j++)
{
int pos = i * 8 + j;
if (pos < c)
{
part[i] |= (count[pos] / n) << (7 - j);
}
}
printf("%d%c", part[i], (i == 3) ? '\n' : '.');
}
memset(part, 0, sizeof(part));
for (int i = 0;i < 4;i++)
{
for (int j = 0;j < 8;j++)
{
int pos = i * 8 + j;
if (pos < c)
{
part[i] |= (1 << (7 - j));
}
}
printf("%d%c", part[i], (i == 3) ? '\n' : '.');
}
}
return 0;
}