原题链接
题意
我们要求找出具有下列性质数的个数(包含输入的正整数 n)。
先输入一个正整数 n(n≤1000),然后对此正整数按照如下方法进行处理:
1.不作任何处理;
2.在它的左边加上一个正整数,但该正整数不能超过原数的一半;
3.加上数后,继续按此规则进行处理,直到不能再加正整数为止。
输入n, 输出一个整数(该整数我在代码中用re[n]来表示)
表示具有该性质的数的个数;
(例如 6 左边加一个不大于 6 的一半的数 2 变为 26 , 2 的左边还可以加不大于 2 的一半的数 1,变为 126)
案例
输入 6
输出 6
案例中的 6 个数是 6,16,26,126,36,136
解析
这道题可以直接递归, 也可以找到它的递推式来做.
一 递推解法
利用递推求解首先就是要找到这道题的递推式;
当我看懂题意后, 我首先罗列了从 1 开始的多个整数在进行操作后输出的结果 re[i], 最终发现了这道题的递推公式就是 re[n] = 1 + re[1] + re[2] + re[3] + …+ re[n/2]; 并且初始条件 re[1] = 1; 找到了递推公式, 知道了初始条件我们就可以用这个递推公式来做这道题了
以下是我利用该递推式写的代码:
#include <bits/stdc++.h>//递推法求解;
using namespace std;
int re[10005], n, ans;
int main() {
cin >> n;
ans = 1;//即递推re[n]的递推公式开头的数字 1;
for (int i = 1; i <= n / 2; i++) {//将re[1] 到 re[n/2]相加求re[n] 的结果;
re[i] = 1;//对于re[1]到re[n/2]利用递推公式, 先赋值为公式开头的 1;
for (int j = 1; j <= i / 2; j++) {//求 1 到 n/2 每个数的r[i];
re[i] += re[j];
}
ans += re[i];
}
cout << ans << endl;
return 0;
}
二 递归解法
这道题还可以利用递归来计算, 但如果单纯的用递归进行回溯,在这道题上就会卡时间(事实上我这样做就是TLE了), 所以又学习了一下, 知道了这里用到了 记忆化搜索 的思想;
就是你要用一个数组来记录你之前已经遍历过的情况, 再次遇到这种情况就不用再次调用函数再算一遍了, 直接把数组中记录这种情况的数输出就行了, 这样就避免了做过多的无用功, 提高了递归的效率;
那么这道题的递归做法就可以用上这种思想了, 下面就是我写的代码了:
#include <bits/stdc++.h>//递归法求解;
using namespace std;
int re[10005], n, ans;
int search(int x) {//;
int temp = 1;//temp我用来代表该次函数运行中 x 的 re[x]当然可以去掉它,直接用re[x]表示也行;
if (re[x] != -1) {
//若re[x] != 0, 就说明 re[x]在之前的函数调用中已经被计算出了, 此时直接返回 r[x]就可以;
return re[x];
}
for (int i = 1; i <= x / 2; i++) {
temp += search(i);
}
return re[x] = temp;
}
int main() {
cin >> n;
memset(re, -1, sizeof(int) * (n + 1));
//memset 一般用来把数组初始化为0或-1, 这里将re数组初始化为 -1, 说明它还未记录数据;
re[1] = 1;//初始条件;
cout << search(n);
return 0;
}