【题目链接】
ybt 1316:【例4.6】数的计数(Noip2001)
ybt 1914:【01NOIP普及组】数的计数
洛谷 P1028 [NOIP2001 普及组] 数的计算
【题目考点】
1.递推
考虑:递推状态,初始状态,递推关系
2.记忆化递归
考虑:递归问题,递归关系,递归出口,状态记录
【解题思路】
解法1:
1)递推:
- 递推状态:a[i]: 数字i经过处理后可以生成的数字有多少个
- 递推关系:思考过程:如果要想求a[n], 对于所有k(k<n),a[k]是已知的。
数字n左边可以添加的数字可以有:0(不添加),1,2,…,n/2。而假设添加数字i( 0 ≤ i ≤ n / 2 0 \le i \le n/2 0≤i≤n/2),那么由数字i可以生成a[i]种数字。
共可以生成数字个数:a[n] = a[0]+a[1]+...+a[n/2]
- 初始状态:a[0]左边不添加数字,自己本身作为一个结果。a[0]的值为1。
2)记忆化递归:
- 递归问题:求数字n经过处理后可以生成的数字有多少个,函数记为int getNum(int n)
- 递归关系:思考过程:如果要想求getNum(n),可以先求getNum(k) (已知k<n)
数字n左边可以添加的数字可以有:0(不添加),1,2,…,n/2。而假设添加数字i( 0 ≤ i ≤ n / 2 0 \le i \le n/2 0≤i≤n/2),那么由数字i可以生成getNum(i)种数字。
总共可以生成数字的种类数为:getNum(0)+getNum(1)+...+getNum(n/2)
- 递归出口:getNum(0)表示左边不添加数字,自己本身作为一个结果。getNum(0)的值应该为1
- 状态记录:数字i能生成的数字数量
解法2:
在解法1的基础上继续化简递推公式
已知递推关系:a[n] = a[0]+a[1]+...+a[n/2-1]+a[n/2]
那么:a[n-1] = a[0]+a[1]+...+a[(n-1)/2]
- 如果n为偶数,那么(n-1)/2 = n/2 - 1
a[n-1] = a[0]+a[1]+...+a[n/2-1]
a[n] = a[n-1] + a[n/2]
- 如果n为奇数,那么(n-1)/2 = n/2
a[n-1] = a[0]+a[1]+...+a[n/2]
a[n] = a[n-1]
(递归写法略)
【题解代码】
解法1:
- 递推, 递推关系:
a[n] = a[0]+a[1]+...+a[n/2]
#include<bits/stdc++.h>
using namespace std;
int a[1005];//a[i]:数字i能生成的数字数量
int main()
{
int n;
cin >> n;
a[0] = 1;
for(int i = 1; i <= n; ++i)
{
for(int j = 0; j <= i/2; ++j)
a[i] += a[j];
}
cout << a[n];
return 0;
}
- 记忆化递归
#include<bits/stdc++.h>
using namespace std;
int num[1005];//num[i]:数字i能生成的数字数量
int getNum(int k)//获取数字k能生成的数字数量
{
if(k == 0)
return 1;
if(num[k] > 0)//记忆化递归
return num[k];
int s = 0;
for(int i = 0; i <= k/2; ++i)//n坐标的数字
s += getNum(i);
return num[k] = s;//记忆状态
}
int main()
{
int n;
cin >> n;
cout << getNum(n);
return 0;
}
解法2:
- 递推 递推关系:如果i是偶数a[i]=a[i-1]+a[i/2],否则a[i]=a[i-1]
#include<bits/stdc++.h>
using namespace std;
int a[1005];//a[i]:数字i能生成的数字数量
int main()
{
int n;
cin >> n;
a[0] = 1;
for(int i = 1; i <= n; ++i)
{//或写为:a[i] = i%2 == 0 ? a[i-1]+a[i/2] : a[i-1];
if(i % 2 == 0)
a[i] = a[i-1] + a[i/2];
else
a[i] = a[i-1];
}
cout << a[n];
return 0;
}
(递归写法略)