本文最后更新于 2025-07-11T11:32:40+08:00
洛谷同文链接
B4219 [常州市程序设计小能手 2023] 数学作业 题解
思路
先输入 $n$,将小于等于 $n$ 的所有斐波那契数提前初始化好,存在数组中。也可以提前用打表处理好。然后用 DFS 对于每一个斐波那契数进行选或不选的搜索即可。
1 2 3 4 5 6 7 8
| void dfs(int i,int sum){ if(sum+a[i]==n){ ans++; break; } dfs(i+1,sum); dfs(i+1,sum+a[i]); }
|
但是,观察一下数据范围:数据范围是 $n\le10^{12}$,而小于等于 $10^{12}$ 的斐波那契数共有大约 $58$ 个,但是我们的算法复杂度为 $O(2^n)$,显然直接就 TLE 了。
于是我们就得想一个优化方法。
优化
使用前缀和优化
我们可以创建前缀和数组 $h$ 使 $h_i$ 表示数列 $f$ 从 $f_0$ 到 $f_{i-1}$的和,若当前选择的斐波那契数和 $s$ 加上后面所有斐波那契数的和还是小于 $n$,则直接退出搜索。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| #include<bits/stdc++.h> using namespace std;
typedef long long ll;
struct node{ int i; ll s; }; int main(){ ll n; cin>>n; vector<ll> fib; if(n>=1){ fib.push_back(1); } if(n>=2){ fib.push_back(2); ll a=1,b=2; while(1){ ll c=a+b; if(c>n) break; fib.push_back(c); a=b; b=c; } } reverse(fib.begin(),fib.end());
int m=fib.size();
if(m==0){ if(n==0) cout<<1; else cout<<0; return 0; } vector<ll> prefix(m+1,0); for(int i=m-1;i>=0;i--){ prefix[i]=fib[i]+prefix[i+1]; }
stack<node> st;
st.push({0,n});
ll ans=0;
while(!st.empty()){ node temp=st.top(); st.pop(); int i=temp.i; ll s=temp.s;
if(s==0){ ans++; continue; } if(i>=m){ continue; }
if(fib[i]>s){ st.push({i+1,s}); } else{ if((i+1<m)?prefix[i+1]<s:0<s){ st.push({i+1,s-fib[i]}); } else{ st.push({i+1,s}); st.push({i+2,s-fib[i]}); } } } cout<<ans; return 0; }
|