第四章 函数和递归(习题篇)--算法竞赛入门经典

第四章 函数和递归(习题篇)

习题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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值