51Nod 1383 整数分解为2的幂
题意
任何正整数都能分解成2的幂,给定整数N,求N的此类划分方法的数量!由于方案数量较大,输出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 <= N <= 10^6)
输出:输出划分方法的数量Mod 1000000007
解题思路
这个题目我开始想到的想法类似于整数划分问题
整数划分问题中,定义假设函数f(n,m):n为要整数划分的数,m为划分中出现的最大加数
这里我进行了一些改变:n还是要进行划分的数,m表示为2的指数,如m=2表示的数是$$2^2$$。
这样$$f(n, m) = fun(n-2^m, m)+fun(n, m-1)$$
上面的公式表示两部分,一部分是使用了$$2^m$$,一部分是没有使用。
然后使用记忆化搜索即可,使用二维的数组来进行记录,注意第二维,我们根据输入的范围是1e6,小于2的22次方,所以第二维大小是22解可以了。
第二种方法
做完这个题后,我搜了这个题,发现可以用递推的方式来进行
if(N%2==0) F[N]=F[N-1]+F[N>>1]
else F[N]=F[N-1]
-
对于一个偶数,可以让比他小一数的方案全部加一个
1
得到,也可以让比他小一半的数的方案元素都乘以2得到。 -
对于奇数只能由比他小的数加一得到。
代码实现
//第一种方式
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<sstream>
typedef long long ll;
using namespace std;
const ll llinf = 0x3f3f3f3f3f3f3f3f;
const int inf=0x3f3f3f3f;
const double eps=1e-6;
const int MAXN=1e6+7;
const ll mod = 1000000007;
ll n, ans;
int dp[MAXN][22];
int dfs(int x, int p){
if(p<0)
return 0;
if(x==0)
return 1;
int y = 1<<p;
if(x<y){
do{
y>>=1;
p--;
}while(x<y);
}
if(dp[x][p]>=0){
return dp[x][p];
}
dp[x-y][p] = dfs(x-y, p) % mod;
dp[x][p-1] = dfs(x, p-1) % mod;
return dp[x][p] = (dp[x-y][p] + dp[x][p-1])%mod ;
}
int main(){
cin>>n;
int max2 = 0;
memset(dp, -1, sizeof(dp));
for(int i=0, tmp = 1; tmp<=n; i++){
max2=i;
tmp <<= 1;
}
// cout<<max2<<endl;
cout<<dfs(n, max2)<<endl;
return 0;
}
//第二种方式
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6;
const int MOD = 1e9 + 7;
int f[N + 1];
int main(){
f[0] = 1;
f[1] = 1;
for(int i = 2; i <= N; i++)
if(i % 2)
f[i] = f[i - 1];
else
f[i] = (f[i - 1] + f[i >> 1]) % MOD;
int n;
while(~scanf("%d", &n))
printf("%d
", f[n]);
return 0;
}