整数划分
【题目描述】
给定整数 \(N\),求分解成\(2\)的幂的方案数。结果\(Mod \ 1000000007\),比如 \(N = 7\) 时,共有 \(6\) 种划分方法。
\(7=1+1+1+1+1+1+1 \\ \ =1+1+1+1+1+2 \\ \ =1+1+1+2+2 \\ \ =1+2+2+2 \\ \ =1+1+1+4 \\ \ =1+2+4 \\\)
【输入格式】
输入一个数\(N\)。\((1 \le N \le 10^6)\)
【输出格式】
输出划分方法的数量 \(Mod \ 000000007\)
第一眼看好像是结论题 于是推了好久的结论。。。没推出来 然后就去写DP
结果到最后也只写了个错误的背包
做法1 完全背包
物品就是\(2^0 2^1 2^2 \cdots\) 然后跑个板子就行了。。。
借下学长代码
#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 1000000
#define mod 1000000007
using namespace std;
typedef long long ll;
int n;
int sz;
int bin[maxn+5];
ll dp[maxn+5];
int main(){
scanf("%d",&n);
bin[0]=1;
while(bin[sz]*2<=n){
sz++;
bin[sz]=bin[sz-1]*2;
}
dp[0]=1;
for(int i=0;i<=sz;i++){
for(int j=bin[i];j<=n;j++){
dp[j]+=dp[j-bin[i]];
dp[j]%=mod;
}
}
printf("%lld\n",dp[n]);
}
时间复杂度 \(O(n log n)\)
做法2 递推
可以分别考虑将当前数字拆分出几个1
设\(f[i]\)代表\(i\)有多少种拆分方案
以\(f[9]\)为例 如果把9拆分成1个1 和 其他一些数字 则方案数就等于(将8用2, 4, 8拆分的方案数) 其实也就是 (将4用1, 2, 4拆分的方案数) 即\(f[4]\)
同理 把9拆分成3个1和其他数字时 方案数会是 \(f[3]\)
所以可以得出结论 \(f[n] = \sum_{i=1}^{n/2} f[i]\) 这个东西用前缀和维护一下就行
这么简单的结论我没想到 我太蒻了
代码
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;
const ll mod = 1000000007;
ll n, ans;
ll a[1000005], sum[1000005];
ll read() {
ll ret = 0, flag = 1;
char ch = getchar();
while (ch > '9' || ch < '0') {
if (ch == '-') flag = -1;
ch = getchar();
}
while (ch <= '9' && ch >= '0') {
ret = (ret << 3) + (ret << 1) + (ch ^ '0');
ch = getchar();
}
return ret * flag;
}
int main() {
n = read();
a[1] = 1; sum[1] = 2;
a[0] = 1; sum[0] = 1;
for (int i = 2; i <= n; i++) {
a[i] = sum[i/2]; sum[i] = (sum[i-1] + a[i]) % mod;
}
printf("%lld\n", a[n]);
return 0;
}
时间复杂度\(O(N)\)