Luogu P4133 [BJOI2012] 最多的方案 题解

记忆化搜索,设 f(x,k) 表示当前考虑 Fk,需要分解的数是 k,分类讨论

  • x=0,则 f(x,k)=1
  • k=1,则 f(x,k)=0
  • x<Fk,则 f(x,k)=f(x,k1)
  • x>F1+F2+F3++Fk1=Fk+11,则 f(x,k)=f(xFk,k1),因为此时不分解出 Fk 就一定无解;
  • 否则,f(x,k)=f(xFk,k1)+f(x,k1)

这个做法很轻松地通过了所有数据,我们如何知道它的时间复杂度?

考虑势能分析,设 Φ(n,k) 表示计算 f(x,k)x 的最大可能值。

断言:要么 Φ(n,k)<Fk+1,要么 Φ(n,k) 在常数次迭代内可以满足前述情况。

证明

边界情况令 Φ(n,k)=n,接下来假设 k>2

考虑在转移过程中说明断言成立。

  • x<Fk,则 Φ(n,k1)<Fk 满足断言;
  • 否则,分类 f(x,k)=f(x,k1)+f(xFk,k1) 中的两项。注意此时 Fkx<Fk+1,所以
    1. 对于 f(xFk,k1),有 0xFk<Fk1,满足断言。进一步,其继续迭代一次仍满足断言;
    2. 对于 f(x,k1),下一次迭代必然走向 f(xFk1,k2),此时 Fx2xFk1<Fk1 满足断言。

断言成立。

Q.E.D.

推论

ϕ=512,则当 k 足够大时,每两次迭代至少使势能变为原来的 2ϕ20.764

证明

k 足够大时,斐波那契数列相邻两项比值 FkFk+1=ϕ

继续上一个证明的分类讨论:

  • 第一类情况中,一次迭代使得势能变为原来的 ϕ,两次即为 ϕ2
  • 第二类情况中,两次迭代使得势能变为原来的 2ϕ2,两种子情况分别贡献 ϕ2
  • 特殊地,如果第一类情况后接着第二类情况,三次为 2ϕ3,平均一次 (2ϕ3)23=43ϕ2<2ϕ2

因此势能至少变为 2ϕ2

Q.E.D.

所以,总迭代次数约为 2log2ϕ2(n)2log1.309(n)O(log1.309(n)),时间复杂度正确。

注意 k 较小时,比值大于 2ϕ2,但由于该估计本身较松,对时间复杂度无影响。

#include <iostream>
#include <map>
using namespace std;
typedef long long i64;
const i64 lim = 1e18;
int tail;
i64 fib[100];
i64 f[100];
map<pair<i64, int>, int> mp;
static inline i64 dfs(i64 x, int k) {
if (!x)
return 1;
if (k == 1)
return 0;
if (mp.count({x, k}))
return mp[{x, k}];
if (x < fib[k])
return mp[{x, k}] = dfs(x, k - 1);
if (f[k - 1] < x)
return mp[{x, k}] = dfs(x - fib[k], k - 1);
return mp[{x, k}] = dfs(x, k - 1) + dfs(x - fib[k], k - 1);
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
fib[1] = fib[2] = 1;
for (tail = 2; fib[tail] <= lim; ++tail)
fib[tail + 1] = fib[tail] + fib[tail - 1];
for (int i = 1; i <= tail; ++i)
f[i] = f[i - 1] + fib[i];
i64 n;
cin >> n;
cout << dfs(n, tail) << '\n';
return 0;
}
posted @   bluewindde  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示