1) 1+1+1+1+1+1+1
2) 1+1+1+1+1+2
3) 1+1+1+2+2
4) 1+1+1+4
5) 1+2+2+2
6) 1+2+4
Help FJ count all possible representations for a given integer N (1 <= N <= 1,000,000).
7Sample Output
6
题意:输入一个整数n,让你求的这个数能分解成多少种形式的整数之和,要求分分解的整数都是2的幂次方的数(幂为大于等于0 )最后结果对 le9 取余;
思路:
刚开始我看到了所有的数都是2的幂次方组成,我就想到了把所有的数都化成二进制,让二进制 位上为1 的往前推,能不能找到个方法写, 最好找状态转移方程,但是往前推得的模拟过程太麻烦了,我找不到好的解法,更别说状态转移方程了
当你没有思路时,因为这是一道动态规划题,找状态转移方程,你要实在找不到的话你就先 打表模拟找规律 ,你要有觉着可以的状态转移方程的话,你最好先尝试对不对,然后再打表。 下面是我用递归打的表,找出前100个数的方法数;
代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#include<math.h>
#define Max 1000000
int a[40];
int sum1 = 0;
int n;
int b[1001];
void dfs(int top,int sum,int tt,int x)
{
if(top<0) return ;
if(sum>tt ) return ;
if(sum==tt)
{
/*for(int i = 0;i<x;i++)
printf("%d ",b[i]);
printf("\n");*/
sum1++;
return ;
}
for(int i = top;i>=0;i--)
{
b[x] = a[i];
dfs(i,sum+a[i],tt,x+1);
}
}
int main()
{
int i,j;
for(i = 0 ;i<32;i++)
{
a[i] = pow(2,i);
if(a[i]>Max)
break;
}
n = i;
for(i = 1;i<=100;i++)
{
sum1 = 0;
dfs(n-1,0,i,0);
printf("i==%d sum1==%d\n",i,sum1);
}
return 0;
}
前100项,你就会看到一个很明显的规律
我这先写出前 11 项的数:
i = 1 2 3 4 5 6 7 8 9 10 11
s = 1 2 2 4 4 6 6 10 10 14 14
很明显的一个规律:
当 i 为 奇数时,a[i] = a[i-1];
当 i 为 偶数时,可能太好看,但要是输出了前100项的话,你觉着你就一定能看出来了
a[ i ] = a[i -1 ] + a[i/2];
下面给大家说说为什么是这个递推式?
当 n 为 奇数 时,可以比前面那个数多个 1,让 n-1 的每个式子都 在末尾加一个1 就行,1也不能分解为2的其他次幂了;所以 a[i] = a[i-1];
当 n 为 偶数时,这种情况要分情况讨论;
当n为偶数时,用式子表示n的话,式子里面要有 1 的话,一定会存在至少两个1 ,所以分 式子中存不存在 1
存在 1 :一定存在至少两个 1 ,所以在 n-2 的每个式子 后都 +1+1;
不存在1 :让 n/2 的每个式子中的数都 乘以2 就是 n的表示方法了;
所以 递推式: a[i] = a[i-2] + a[i/2];
下面说一句,因为找递推 找式子表示的方法数, 你是不是要从式子上入手,找递推式,一般都会和前一项 有关系,多做动态规划题,根据题意上的限制,找出递推式;
AC代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define mod 1000000000
#define ll long long
#define Max 1000000
ll a[Max+10];
int main()
{
int i,j;
a[1] = 1;
a[2] = 2;
a[3] = 2;
a[4] = 4;
a[5] = 4;
for(i = 6;i<=Max;i++)
{
if(i%2) a[i] = a[i-1]%mod;
else a[i] = (a[i-2] + a[i/2])%mod;
}
int n;
while(~scanf("%d",&n))
{
printf("%lld\n",a[n]);
}
return 0;
}