洛谷题单算法1-4递推与递归

P1255 数楼梯

题目本质是 Fibonacci 数列

走楼梯,要么一次走一格,要么一次走两格

那每次的走法 = 上一格的方案数 + 上上格的方案数

这就有了递推公式:

a[i] = a[i - 1] + a[i - 2]

这题要注意数据,开 long long 也只能得60points :

#include<iostream>
using namespace std;
long long a[10000];
int main(){ 
	int n;
	cin >> n;
	a[1] = 1; a[2] = 2;
	for (int i = 3; i <= n; i++)
		a[i] = a[i - 1] + a[i - 2];
	cout << a[n];
	return 0;
}

所以需要用高精度算法,注意 n=1 || n=2 时候的特殊判断 :

#include<iostream>
using namespace std;
int a[5000], b[5000], c[5000];
void jiafa(int a[], int b[]) {
	int jw = 0;
	for (int i = 0; i < 5000; i++) {
		c[i] = (a[i] + b[i] + jw) % 10;
		jw = (a[i] + b[i] + jw) / 10;
	}
}
int main(){ 
	int n;
	cin >> n;
	if (n == 2 || n == 1) {
		cout << n;
	}
	else {
		a[0] = 1; b[0] = 2;
		for (int i = 3; i <= n; i++) {
			jiafa(a, b);
			for (int i = 0; i < 5000; i++)
				a[i] = b[i], b[i] = c[i];
		}
		int i = 4999;
		for (i; i >= 0; i--) {
			if (c[i] != 0)
				break;
		}
		for (i; i >= 0; i--)
			cout << c[i];
	}
	return 0;
}

P1002 [NOIP2002 普及组] 过河卒

卒要么向下走要么向右走

那么每次的走法 = 左一格的方案数 + 上一格的方案数

这就有了递推公式:

a[i][j] = a[i - 1][j] + a[i][j - 1]

首先由于数据比较大开数组时选择long long( 十年OI一场空,不开浪浪见祖宗 )

紧接着初始化二维数组:有马的地方都不可以过去,因此都初始为0,如下图 ( 以样例为例 )

初始化时要注意如果第0行或者第0列有马时 (或者马可以跳到这种位置) 的特殊情况,如下图:

此时第0行或者第0列都应该开始变为0

接着就利用递推公式开始完善二维数组,注意有马的地方不需要赋值,如下图 ( 以样例为例 )

#include<iostream>
using namespace std;
long long a[26][26];
int main(){ 
	a[0][0] = 0;
	for (int i = 1; i < 25; i++)
		a[0][i] = 1, a[i][0] = 1;
	int bx, by, cx, cy;
	cin >> bx >> by >> cx >> cy;
	if (cx >= 0 && cy >= 0)a[cx][cy] = 0;
	if (cx + 2 >= 0 && cy + 1 >= 0)a[cx + 2][cy + 1] = 0;
	if (cx + 1 >= 0 && cy + 2 >= 0)a[cx + 1][cy + 2] = 0;
	if (cx - 1 >= 0 && cy + 2 >= 0)a[cx - 1][cy + 2] = 0;
	if (cx - 2 >= 0 && cy + 1 >= 0)a[cx - 2][cy + 1] = 0;
	if (cx - 2 >= 0 && cy - 1 >= 0)a[cx - 2][cy - 1] = 0;
	if (cx - 1 >= 0 && cy - 2 >= 0)a[cx - 1][cy - 2] = 0;
	if (cx + 1 >= 0 && cy - 2 >= 0)a[cx + 1][cy - 2] = 0;
	if (cx + 2 >= 0 && cy - 1 >= 0)a[cx + 2][cy - 1] = 0;
	int x = 1;
	while (a[x][0] == 1)
		x++;
	for (x; x < 25; x++)
		a[x][0] = 0;
	int y = 1;
	while (a[0][y] == 1)
		y++;
	for (y; y < 25; y++)
		a[0][y] = 0;
	for (int i = 1; i < 25; i++) {
		for (int j = 1; j < 25; j++) {
			if ((i == cx && j == cy) || (i == cx + 2 && j == cy + 1) || (i == cx + 1 && j == cy + 2) || (i == cx - 1 && j == cy + 2) || (i == cx - 2 && j == cy + 1) || (i == cx - 2 && j == cy - 1) || (i == cx - 1 && j == cy - 2) || (i == cx + 1 && j == cy - 2) || (i == cx + 2 && j == cy - 1))
				continue;
			else
				a[i][j] = a[i - 1][j] + a[i][j - 1];
		}
	}
	cout << a[bx][by];
	return 0;
}

P1044 [NOIP2003 普及组] 栈

这题本质是 Catalan数,可直接用数论方法来 AC ,当然也可以递推:

创建二维数组 a [ i ] [ j ]  表示此时(栈内有 i 个数,未进栈的有 j 个数)有多少种情况

由于栈内数字要么出栈pop:则栈内元素减一,a [ i - 1 ] [ j ] 

                    要么压栈push:则栈内元素加一,未进栈元素减一,a [ i + 1 ] [ j - 1 ] 

而当栈内没有元素时:则只能进栈,a [ i + 1 ] [ j - 1 ] 

这就有了递推公式:

a[i][j] = a[i - 1][j] + a[i + 1][j - 1]
a[i][j] = a[i + 1][j - 1]

首先初始化二维数组:当 a [ i ] [ 0 ] 时,i 个元素全部进栈,故只有一种情况,即a [ i ] [ 0 ] = 1  

如下图:

紧接着初始化二维数组:注意根据递推公式要一列一列的赋值,如下图:

最后输出 a [ 0 ] [ n ] 即可

#include<iostream>
using namespace std;
int a[20][20];
int main(){ 
	int n; cin >> n;
	for (int i = 1; i < 20; i++)
		a[i][0] = 1;
	for (int j = 1; j < 20; j++) {
		for (int i = 0; i < 20; i++) {
			if (i == 0)
				a[i][j] = a[i + 1][j - 1];
			else
				a[i][j] = a[i - 1][j] + a[i + 1][j - 1];
		}
	}
	cout << a[0][n];
	return 0;
}

P1028 [NOIP2001 普及组] 数的计算

通过简单的枚举,可以发现递推规律:a [ i ] =1 + a[ 1 ] + a[ 2 ] +...+ a[ i/2 ]

此题数据较小,开 int 也是可以过的,保险起见还是开浪浪:

#include<iostream>
using namespace std;
long long a[1001];
int main(){ 
	int n; cin >> n;
	a[1] = 1, a[2] = 2, a[3] = 2;
	if (n >= 4) {
		for (int i = 4; i <= 1000; i++) {
			long long ans = 1;
			int x = i / 2;
			for (int j = 1; j <= x; j++) {
				ans += a[j];
			}
			a[i] = ans;
		}
		cout << a[n];
	}
	else
		cout << a[n];
	return 0;
}

P1464 Function

乍一看题目,纯纯递归嘛,直接上代码:

#include<iostream>
using namespace std;
long long w(long long a, long long b, long long c) {
	if (a <= 0 || b <= 0 || c <= 0)
		return 1;
	if (a > 20 || b > 20 || c > 20)
		return w(20, 20, 20);
	if (a < b && b < c)
		return w(a, b, c - 1) + w(a, b - 1, c - 1) - w(a, b - 1, c);
	else
		return w(a - 1, b, c) + w(a - 1, b - 1, c) + w(a - 1, b, c - 1) - w(a - 1, b - 1, c - 1);
}
int main(){ 
	long long a, b, c;
	while (scanf("%lld %lld %lld", &a, &b, &c)) {
		if (a == -1 && b == -1 && c == -1)
			break;
		printf("w(%lld, %lld, %lld) = ", a, b, c);
		printf("%lld\n", w(a, b, c));
	}
	return 0;
}

好的,喜提 0point:数据都能过,但是全部超时,(ˉ▽ˉ;)...,

因此必须使用记忆化搜索每次计算后用一个数组来记录答案,这样下一次计算时就直接调用数组内存好的答案就行了,避免了不必要的重复计算,大大节省时间

#include<iostream>
using namespace std;
long long fun[50][50][50];
int w(long long a, long long b, long long c) {
	if (a <= 0 || b <= 0 || c <= 0)
		return 1;
	if (a > 20 || b > 20 || c > 20)
		return w(20, 20, 20);
	if (fun[a][b][c] != 0)
		return fun[a][b][c];
	if (a < b && b < c)
		return fun[a][b][c] = w(a, b, c - 1) + w(a, b - 1, c - 1) - w(a, b - 1, c);
	else
		return fun[a][b][c] = w(a - 1, b, c) + w(a - 1, b - 1, c) + w(a - 1, b, c - 1) - w(a - 1, b - 1, c - 1);
}
int main(){ 
	long long a, b, c;
	while (scanf("%lld %lld %lld", &a, &b, &c)) {
		if (a == -1 && b == -1 && c == -1)
			break;
		printf("w(%lld, %lld, %lld) = ", a, b, c);
		printf("%lld\n", w(a, b, c));
	}
	return 0;
}

P2437 蜜蜂路线

题目本质是 Fibonacci 数列,与数楼梯一样,开浪浪只能得 40points:

#include<iostream>
using namespace std;
long long a[1001];
int main(){ 
	int m, n; cin >> m >> n;
	int x = n - m + 1;
	a[1] = 0, a[2] = 1, a[3] = 2;
	for (int i = 4; i <= 1000; i++) {
		a[i] = a[i - 1] + a[i - 2];
	}
	cout << a[x];
	return 0;
}

因此使用高精度加法算法:

#include<iostream>
using namespace std;
int a[300], b[300], c[300];
void jiafa(int a[], int b[]) {
	int jw = 0;
	for (int i = 0; i < 300; i++) {
		c[i] = (a[i] + b[i] + jw) % 10;
		jw = (a[i] + b[i] + jw) / 10;
	}
}
int main(){ 
	int m, n; cin >> m >> n;
	int x = n - m + 1;
	a[0] = 1, b[0] = 1;
	for (int i = 3; i <= x; i++) {
		jiafa(a, b);
		for (int i = 0; i < 300; i++)
			a[i] = b[i], b[i] = c[i];
	}
	int i = 299;
	for (i; i >= 0; i--)
		if (c[i] != 0)
			break;
	for (i; i >= 0; i--)
		cout << c[i];
	return 0;
}

P1164 小A点菜

创建二维数组 b [ i ] [ j ]  表示此时( i 个菜,花 j 元)有多少种情况

动态规划:当钱和价格相等时  b [ i ][ j ] = b[ i - 1][ j ] + 1

                  当钱大于价格时     b [ i ][ j ] = b[ i - 1][ j ] + b[ i - 1][ j - a[ i ] ]

                  当钱小于价格时     b [ i ][ j ] = b[ i - 1][ j ]

#include<iostream>
using namespace std;
int a[101];
int b[101][10001];
int main(){ 
	int n, m; cin >> n >> m;
	for (int i = 1; i <= n; i++)
		cin >> a[i];
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			if (j == a[i])
				b[i][j] = b[i - 1][j] + 1;
			if (j > a[i])
				b[i][j] = b[i - 1][j] + b[i - 1][j - a[i]];
			if (j < a[i])
				b[i][j] = b[i - 1][j];
		}
	}
	cout << b[n][m];
	return 0;
}

P1036 [NOIP2002 普及组] 选数

#include<iostream>
using namespace std;
int n, k;
int a[25];
long long ans;
//判断素数函数
int ispirme(int x) {
	for (int i = 2; i * i < x; i++) {
		if (x % i == 0)
			return 0;
	}
	return 1;
}
//深度搜索 dfs ( 开始选数,已经选好数字的个数,选好所有数的和 )
void dfs(int start, int cnt, int sum) {
	int i;
	//如果选中的个数刚好满足,直接判断是否为素数
	if (cnt == k && ispirme(sum))
		ans++;
	//从开始选数的地方循环到 n
	for (i = start; i <= n; i++)
		//( 从下一个数开始选,已经选好数字的个数加一,并把这个数加上)
		dfs(i + 1, cnt + 1, sum + a[i]);
}
int main(){ 
	cin >> n >> k;
	for (int i = 1; i <= n; i++)
		cin >> a[i];
	dfs(1, 0, 0);
	cout << ans;
	return 0;
}

P3612 [USACO17JAN] Secret Cow Code S

暴力枚举:创建两个超大一维数组,分别进行来回赋值,最后把需要的所有字母赋值到一个数组里,最后直接输出第 n 个位置的字母即可,但是这样做会超空间,只能得 40points:

#include<iostream>
using namespace std;
char ch[100000000];
char chx[100000000];
int main(){ 
	int i = 0, cnt = 0;
	scanf("%c", &ch[i]);
	while (1) {
		if (ch[i] == ' ')
			break;
		i++; cnt++;
		scanf("%c", &ch[i]);
	}
	for (int i = 0; i < cnt; i++)
		chx[i] = ch[i];
	long long n, len; cin >> n;
	len = n + 1 + cnt;
	n--;
	while (len > cnt) {
		chx[cnt] = ch[cnt - 1];
		for (int i = cnt + 1; i < 2 * cnt; i++)
			chx[i] = ch[i - cnt - 1];
		cnt *= 2;
		for (int i = 0; i < cnt; i++)
			ch[i] = chx[i];
	}
	cout << chx[n];
	return 0;
}

因此需要采用分治和递归思想:

第一步还是通过 n 找 max:

long long max = 1;
long long n;
long long flag = n / cnt + 1;
while (max < flag)
	max <<= 1;
max *= cnt;

第二步来判断 n 的类型,共有三种:

第一种:n 在右半部分的第一个位置,那么它在左半部分的位置就是最后一个,即:

n--;

第二种:n 在右半部分但是不在第一个位置,那么它在左半部分的位置就是减去一半的长度再减一,即:

n = n - (max >> 1) - 1;

第三种:n 在左半部分,这种情况直接长度减半即可:

max >>= 1;

最后输出即可

注意:>>n 是除以(2的n次方),<<n 是乘以(2的n次方),位运算相对会快些

#include<iostream>
using namespace std;
char ch[35];
int main(){ 
	int i = 0, cnt = 0;
	scanf("%c", &ch[i]);
	while (1) {
		if (ch[i] == ' ')
			break;
		i++; cnt++;
		scanf("%c", &ch[i]);
	}
	long long max = 1; long long n; cin >> n;
	long long flag = n / cnt + 1;
	while (max < flag)
		max <<= 1;
	max *= cnt;
	long long x = n - (max >> 1);
	while (n > cnt) {
		if (x == 1)
			n--;
		if (x > 1)
			n = n - (max >> 1) - 1;
		max >>= 1;
	    x = n - (max >> 1);
	}
	cout << ch[n - 1] << endl;
	return 0;
}

P1010 [NOIP1998 普及组] 幂次方

#include <cmath> //其中有log2(x)和pow(x,y)函数

边分解边打印:

首先利用 log2() 将 n 分解出最大项 temp

接着判断 temp 的值,共三类值:temp==0,temp==1,temp > 1,其中前两种情况是直接打印即可,第三种情况通过递归打印

每一次判断打印结束,都要把 x 减去这一项的值,接着继续循环

注意:第一项是不需要 + 的,而后面的每一项都需要 +,因此需要 flag 来进行判断

#include<iostream>
#include<cmath>
using namespace std;
void show(int x) {
	int flag = 0;
	while (x != 0) {
		int temp = int(log2(x));
		if (flag == 1)
			cout << "+";
		if (temp == 1)
			cout << "2";
		else if (temp == 0)
			cout << "2(0)";
		else {
			cout << "2(";
			show(temp);
			cout << ")";
		}
		x -= pow(2, temp);
		flag = 1;
	}
}
int main(){
	int n; cin >> n;
	show(n);
	return 0;
}

P1498 南蛮图腾

这道题目数据很小,直接 模拟 来做的话,也是可以 AC 的:

首先创建一个大一点的二维数组,来存储字符:

char ch[2050][2050];

初始化为 “ 空 ” :

	for (int i = 0; i < 2050; i++)
		for (int j = 0; j < 2050; j++)
			ch[i][j] = ' ';

把 n == 1 的图腾赋值:

需要注意的是:字符 ' \ ' 的赋值应该写成 ' \\ ' 

目的是:用于表示一个反斜杠,防止它被解释为一个转义序列符

	ch[0][1] = '/', ch[0][2] = '\\';
	ch[1][0] = '/', ch[1][1] = '_', ch[1][2] = '_', ch[1][3] = '\\';

即:

开始模拟:将上图往下平移,再往右下平移,即赋值操作:

		for (int i = a; i <= b; i++)
			for (int j = 0; j < b; j++)
				ch[i][j] = ch[i - a][j];
		for (int i = a; i <= b; i++)
			for (int j = b; j < c; j++)
				ch[i][j] = ch[i][j - b];

即:

接着把上方的字符清空:

		for (int i = 0; i < a; i++)
			for (int j = 0; j < b; j++)
				if(ch[i][j]!=' ')
					ch[i][j] = ' ';

即:

最后再把下方的左侧图腾再赋值到上方中间的位置:

		for (int i = 0; i < a; i++)
			for (int j = a; j < a + b; j++)
				ch[i][j] = ch[i + a][j - a];

即:

#include<iostream>
#include<cmath>
using namespace std;
char ch[2050][2050];
void show(int n) {
	int x = 1;
	while (x < n) {
		int a = pow(2, x), b = pow(2, x + 1), c = pow(2, x + 2);
		for (int i = a; i <= b; i++)
			for (int j = 0; j < b; j++)
				ch[i][j] = ch[i - a][j];
		for (int i = a; i <= b; i++)
			for (int j = b; j < c; j++)
				ch[i][j] = ch[i][j - b];
		for (int i = 0; i < a; i++)
			for (int j = 0; j < b; j++)
				if(ch[i][j]!=' ')
					ch[i][j] = ' ';
		for (int i = 0; i < a; i++)
			for (int j = a; j < a + b; j++)
				ch[i][j] = ch[i + a][j - a];
		x++;
	}
}
int main(){
	for (int i = 0; i < 2050; i++)
		for (int j = 0; j < 2050; j++)
			ch[i][j] = ' ';
	ch[0][1] = '/', ch[0][2] = '\\';
	ch[1][0] = '/', ch[1][1] = '_', ch[1][2] = '_', ch[1][3] = '\\';
	int n; cin >> n;
	show(n);
	int high = pow(2, n);
	int len = pow(2, n + 1);
	for (int i = 0; i < high; i++) {
		for (int j = 0; j < len; j++) {
			cout << ch[i][j];
		}
		cout << endl;
	}
	return 0;
}

P1928 外星密码

这道题目直接 模拟 来做的话,也是可以 AC 的:

以输入 AC [ 2E [ 3CD ] [ 2B ] ] YU [ 3R ]  为例模拟一次:

字符串中从0开始遍历找第一个 ] ,再从该位置向左找第一个 [ 

得:AC [ 2E [ 3CD ] [ 2B ] ] YU [ 3R ] 

记录数字 3 ,创建临时字符串 CD ,

将字符串中 [ ] 的部分删除,得 AC [ 2E [ 2B ] ] YU [ 3R ]

再把临时字符串加到原字符串中,得:AC [ 2ECDCDCD[ 2B ] ] YU [ 3R ]

这样第一次模拟就完成了

#include<iostream>
using namespace std;
string str = "";
int len, l, r;
//判断函数,当字符串无需解压缩时返回1,需要解压缩时返回0
int judge(string a) {
	for (int i = 0; i < a.size(); i++)
		if (a[i] == '[')
			return 0;
	return 1;
}
//模拟解压缩函数
string jieyasuo(string s) {
	len = s.size();
	for (int i = 0; i < len; i++) {
		if (s[i] == ']') {
            //找到 ] 的位置
			r = i;
			for (int j = r; j >= 0; j--) {
				if (s[j] == '[') {
                    //找到 [ 的位置
					l = j;
					int cnt = 0;
					int ll = l;
                    //判断数字位数,并且记录数字
					if (s[j + 2] >= '0' && s[j + 2] <= '9') {
						cnt = 10 * (s[j + 1] - '0') + (s[j + 2] - '0');
						ll += 2;
					}
					else {
						cnt = (s[j + 1] - '0');
						ll += 1;
					}
                    //创建临时字符串
					string temp = "";
					for (int k = ll + 1; k < r; k++)
						temp += s[k];
                    //清除原字符串需要解压缩的部分
					s.erase(l, r - l + 1);
                    //开始解压缩
					while (cnt--) {
						s.insert(l, temp);
					}
                    //清除临时字符串
					temp.clear();
                    //返回解压后的字符串
					return s;
				}
			}
		}
	}
}
int main() {
	cin >> str;
	while (judge(str) != 1)
		str = jieyasuo(str);
	cout << str << endl;
	return 0;
}

P1990 覆盖墙壁

这题目最重要的就是推出递推公式

化简:

因此最后的递推公式为:

f[i] = 2 * f[i - 1] + f[i - 3] ;

注意:题目要求最多输出四位,因此每次在递推的同时,对数组 f [ i ] % 10000 即可

#include<iostream>
using namespace std;
int n;
int f[1000005];
int main() {
	cin >> n;
	f[1] = 1, f[2] = 2, f[3] = 5;
	if (n >= 4)
		for (int i = 4; i <= n; i++) {
			f[i] = 2 * (f[i - 1] % 10000) + f[i - 3] % 10000;
			f[i] %= 10000;
		}
	cout << f[n];
	return 0;
}

P1259 黑白棋子的移动

这个题目测试数据较小,纯模拟是可以 AC 的,以样例为例进行分析:n = 7

o o o o o o o * * * * * * * - - 交换该行的红色部分后得到下一行
o o o o o o - - * * * * * * o * 交换该行的红色部分后得到下一行
o o o o o o * * * * * * - - o * 交换该行的红色部分后得到下一行
o o o o o - - * * * * * o * o * 交换该行的红色部分后得到下一行
o o o o o * * * * * - - o * o * 交换该行的红色部分后得到下一行
o o o o - - * * * * o * o * o * 交换该行的红色部分后得到下一行
o o o o * * * * - - o * o * o * 交换该行的红色部分后得到下一行
o o o - - * * * o * o * o * o * 最后四行没有明显的规律,可放到最后进行依次输出
o o o * o * * - - * o * o * o *
o - - * o * * o o * o * o * o *
o * o * o * - - o * o * o * o *
- - o * o * o * o * o * o * o *

而上面的规律就是:

如果第一个 o* 在 -- 的左边,则将两者交换即可

如果第一个 o* 在 -- 的右边,则将 -- 和最后两个 ** 交换即可

#include<iostream>
using namespace std;
int n;
char chess[205];
void last() {            //打印最后没有规律的字符串
	int temp;
	temp = n - 4;
	cout << "ooo*o**--*";
	while (temp--)
		cout << "o*";
	cout << endl;
	temp = n - 4;
	cout << "o--*o**oo*";
	while (temp--)
		cout << "o*";
	cout << endl;
	temp = n - 4;
	cout << "o*o*o*--o*";
	while (temp--)
		cout << "o*";
	cout << endl;
	temp = n - 4;
	cout << "--o*o*o*o*";
	while (temp--)
		cout << "o*";
	cout << endl;
}
void show(char a[], int cnt) {     //打印字符串函数
	for (int i = 1; i <= cnt; i++)
		cout << a[i];
	cout << endl;
}
void move(char a[], int cnt) {     //模拟交换函数(即规律)
	int flag = 1;
	int key = 1;
	for (int i = 1; i <= cnt - 1; i++)
		if (a[i] == '-' && a[i + 1] == '-')
			flag = i;
	for (int i = 1; i <= cnt - 1; i++) {
		if (a[i] == 'o' && a[i + 1] == '*') {
			key = i;
			break;
		}
	}
	if (key < flag) {
		a[flag] = a[key], a[flag + 1] = a[key + 1];
		a[key] = '-', a[key + 1] = '-';
	}
	else {
		a[flag] = '*', a[flag + 1] = '*';
		a[key - 1] = '-', a[key - 2] = '-';
	}
}
int main() {
	cin >> n;
	int len = n * 2 + 2;
    //初始化chess数组
	for (int i = 1; i <= n; i++)
		chess[i] = 'o';
	for (int i = n + 1; i <= 2 * n; i++)
		chess[i] = '*';
	chess[2 * n + 1] = '-', chess[2 * n + 2] = '-';
    //先进行第一次打印
	show(chess, len);
    //开始规律模拟,依次调用函数,进行规律输出
	int temp = 2 * (n - 4) + 1;
	while (temp--) {
		move(chess, len);
		show(chess, len);
	}
    //最后几行没有规律,直接输出
	last();
	return 0;
}

P1228 地毯填补问题

分治思想:

以题例为例:先将 n*n 分成4个 (n/2)*(n/2) 的区域

接着判断公主的位置,为左上区域,故选择一号毛毯进行填充,并且输出一号毛毯的坐标

然后4个小区域分别进行递归

左上区域中包含公主,故公主位置不变

右上区域中被一号毛毯覆盖一个位置(4,5),故此位置就是该区域的公主位置

左下区域中被一号毛毯覆盖一个位置(5,4),故此位置就是该区域的公主位置

右下区域中被一号毛毯覆盖一个位置(5,5),故此位置就是该区域的公主位置

最后毛毯可被覆盖成功:

#include<iostream>
#include<cmath>
using namespace std;
int k, x, y;
void dfs(int a, int b, int n, int x, int y) {
	if (n == 1)    // n == 1 时直接退出
		return;
	n /= 2;        //每次将 n*n 拆成4个 (n/2)*(n/2) 的区域
	if (x <= a + n - 1 && y <= b + n - 1) {
    //公主在左上方包括边界
		cout << a + n << " " << b + n << " 1" << endl; //输出
		dfs(a, b, n, x, y);                //递归左上区域(公主位置不变)
		dfs(a, b + n, n, a + n - 1, b + n);//递归右上区域
		dfs(a + n, b, n, a + n, b + n - 1);//递归左下区域
		dfs(a + n, b + n, n, a + n, b + n);//递归右下区域
	}
	else if (x <= a + n - 1 && y >= b + n) {
    //公主在右上方包括边界
		cout << a + n << " " << b + n - 1 << " 2" << endl; //输出
		dfs(a, b, n, a + n - 1, b + n - 1);//递归左上区域
		dfs(a, b + n, n, x, y);            //递归右上区域(公主位置不变)
		dfs(a + n, b, n, a + n, b + n - 1);//递归左下区域
		dfs(a + n, b + n, n, a + n, b + n);//递归右下区域
	}
	else if (x >= a + n && y <= b + n - 1) {
    //公主在左下方包括边界
		cout << a + n - 1 << " " << b + n << " 3" << endl; //输出
		dfs(a, b, n, a + n - 1, b + n - 1);//递归左上区域
		dfs(a, b + n, n, a + n - 1, b + n);//递归右上区域
		dfs(a + n, b, n, x, y);            //递归左下区域(公主位置不变)
		dfs(a + n, b + n, n, a + n, b + n);//递归右下区域
	}
	else {
    //公主在右下方包括边界
		cout << a + n - 1 << " " << b + n - 1 << " 4" << endl; //输出
		dfs(a, b, n, a + n - 1, b + n - 1);//递归左上区域
		dfs(a, b + n, n, a + n - 1, b + n);//递归右上区域
		dfs(a + n, b, n, a + n, b + n - 1);//递归左下区域
		dfs(a + n, b + n, n, x, y);        //递归右下区域(公主位置不变)
	}
	return;
}
int main() {
	cin >> k;
	int n = pow(2, k);
	cin >> x >> y;
    //从 (1,1) 开始到 (n,n) 此时公主为(x,y)
	dfs(1, 1, n, x, y);
	return 0;
}
  • 22
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值