题目链接:P1028 NOIP 2001 普及组 数的计算 - 洛谷

我的解法:
递归 + 双指针
cpp
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
vector<long long> arr, m;
int find(vector<long long>& arr, int le, int rt)
{
int l = le, r = rt, dest = 0;
int ans = 0;
if (r == 0)
{
return 0;
}
if (m[r] != -1) return m[r];
while (l < r)
{
if (arr[l] <= (arr[r] / 2))
{
ans += 1;
dest = l;
}
l++;
}
while (dest) ans += find(arr, 0, dest--);
return m[r] = ans;
}
int main()
{
int n, coun = 1;
cin >> n;
arr.push_back(n);
n /= 2;
while (n > 0)
{
arr.push_back(n);
n--;
}
sort(arr.begin(), arr.end());
int l = 0, r = arr.size() - 1;
m.assign(r + 1, -1);
coun += find(arr, l, r);
cout << coun;
return 0;
}
思路:
先将所有可能构成"合法数列"的值导入数组arr:
首先是n自己,其次发现 ≤ n / 2的数都可以(因为都 ≤ n 的一半)
然后从小到大排序
下面以n = 100举例

之后思考递归怎么写:
首先要构成"合法数列",数列中有一个元素的情况就一种:
也就是n自己
所以coun初始值为1
之后递归函数f为求出构成"合法数列"时,元素 ≥ 1的情况
在f函数里,先解决两个元素的情况:
cpp
int find(vector<long long>& arr, int le, int rt)
{
int l = le, r = rt, dest = 0;
int ans = 0;
if (r == 0)
{
return 0;
}
while (l < r)
{
if (arr[l] <= (arr[r] / 2))
{
ans += 1;
dest = l;
}
l++;
}
基线条件一会再说
while循环就实现了找出所有二元数组:
左指针不断向前移动,满足小于当前右指针的数的一半这个条件时,将数组个数ans++(代表找到一个二元数组)
并且记录一下最后出现的有效左指针,作为下一步循环的右指针
(因为要组成三元或多元数组,必须还要小于上一个数的一半)

求多元的数组就可以把上一个元数"视而不见",继续进行当前的寻找二元数组,就可以实现查找多元数组的效果


但是需要考虑另外一个种情况:
如果我们下一步开始递归只写
cpp
ans += find(arr, 0, dest);
就只能做到跳跃性查找:
100 到 50 到 25 到 ...
但是100 后可以取49 48
而50后又可以取24 23
所以要这样写:
cpp
while (dest) ans += find(arr, 0, dest--);
以其实100举例:
这就实现了 50 - 1所有可能数的取值
之后再返回ans就好了。
但这样为止会TLE
所以又加了记忆数组,避免重复的计算
最佳解法:
递进 + 前缀和
cpp
#include<iostream>
#include<vector>
using namespace std;
int main()
{
int n;
cin >> n;
vector<long long> f(n + 1, 0), s(n + 1, 0);
for (int i = 1; i <= n; i++)
{
f[i] = 1 + s[i / 2];
s[i] = f[i] + s[i - 1];
}
cout << f[n] << endl;
}
这个解法是从底层算起,不断往上算出:
f(n) 为以n结尾的合法数列个数
算f(n) 满足:
最后1为本身自己构成的
cpp
f[i] = 1 + s[i / 2];
s[i] = f[i] + s[i - 1];
s数列是为了避免重复计算,代表的意义是公式中的
当i为1时
f(1) = 1 + s0,s0 = 0;
所以f(1) = 1;
并更新当前i的前边几项和
s1 = 1;
当i为2时:
f(2) = 1 + s1,s1 = 1
f(2) = 2;
s2 = 3;
当i为3时:
f(3) = 1 + s1
s1 = 1
f(3) = 2;
s3 = 5;
............