蓝桥杯:数的划分 记忆搜索解法
问题描述
一个正整数可以划分为多个正整数的和,比如n=3时:
3;1+2;1+1+1;
共有三种划分方法。
给出一个正整数,问有多少种划分方法。
输入格式
一个正整数n
输出格式
一个正整数,表示划分方案数
样例输入
3
样例输出
3
数据规模和约定
n<=100
思路
- 任何正整数 n 都可以拆分为 n 个 1 相加,那么可以得到最长的相加的序列是 n 个 1 相加
- 任何正整数 n 都可以拆分为 1 个 n 加上 1 个 0,那么可以得到最短的序列是 n + 0
n 个 1 对应的情况是:每一份划分的值不大于 1
n 加 0 对应的情况是:每一份划分的值为 n
一次递归分析:
如果我们想将 x 分割为若干份,每一份分割的值 <= max,那么有两种情况:
- x >= max 时: 又有两种情况
- 如果其中一份分割的值为max (这一份的分法已经确定),剩下的所有分割之和为 x-max,那么问题就被分割为子问题:即求解 【x-max 可以被分割成值不超过 max 的若干分割块】的分法数目
- 如果每一份分割的值都小于max,那么问题的解就变成:【将x分割为若干份,每一份分割的值不超过max-1】的分发数目 x >= max 情况的解就是上面两个情况的解之和
- x < max 时: 【将 x 分割为若干分,每一份分割的值不超过max】的分法的数目 = 【将x分割为若干分,每一份分割的值不超过 x 】 的分法的数目,于是问题变为求解【将x分割为若干分,每一份分割的值不超过 x 】的分法数目
结束条件
- 如果将 n 分割,每一份分割块的值不超过1,即 n 个 1 相加,有且只有一种情况,返回1
- 如果将 0 分割,每一份分割块的最大值任意,那么还是 一个 0 ,返回1
- 如果 分割块的最大值 = 1,那么就是 n 个 1 加和,也是返回1
AC完整代码
- 直接递归,会有一个样例过不了,就是100,会超时一点,故用记忆搜索优化一下
#include <iostream>
#include <cstring>
using namespace std;
int res[114][114] = {0};
// 返回将n分割成若干块,每一块的值<=max 的分法数目
int num(int x, int max)
{
if(x==1 || max==1 || x==0)
{
return 1;
}
if(x >= max)
{
int r1;
if(res[x][max-1])
{
r1 = res[x][max-1];
}
else
{
r1 = num(x, max-1);
res[x][max-1] = r1;
}
int r2;
if(res[x-max][max])
{
r2 = res[x-max][max];
}
else
{
r2 = num(x-max, max);
res[x-max][max] = r2;
}
return r1 + r2;
}
else
{
if(res[x][x])
{
return res[x][x];
}
else
{
int r = num(x, x);
res[x][x] = r;
return r;
}
}
}
int main()
{
int n;
cin>>n;
cout<<num(n, n)<<endl;
return 0;
}