Description
Farmer John commanded his cows to search for different sets of numbers that sum to a given number. The cows use only numbers that are an integer power of 2. Here are the possible sets of numbers that sum to 7:
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).
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).
题目大概意思就是给一个数N,然后将它分成若干的2的幂组成的和,问有多少种不同的分法。
这题目通过率还是比较高,可能方法也比较多。我就详细地说一下我自己用的方法,复杂度O(n)。
首先,为了避免重复计数,定义一下C(i)表示分法中最大数为i的总共方法数,最终结果S(N) = C(1) + C(2) + C(4) +....+ C(2 ^ x)。
现在只要求出C(i)就能得到最终答案.
用一棵树来表示一下。
(没有用专门的作图软件,排版也比较乱。)
解释一下图的含义:这个图是N=7时的例子图,设每个节点的深度为d,节点的值为v,就是代表最大值为2^d出现了V次。根节点的深度为0,所以根节点的值就为N,因为划分的元素必须是2的次幂,所以可以知道每一次的划分都是在上一次划分的基础上选择两个相同的值相加后得到新的划分。
所以N=7时,根节点为7代表最大值为1出现了7次,也就是最初始的划分,元素全是1,接着我们想得到最大值为2的划分分案,即在此基础上,分别选择2个,4个,6个1进行两两相加得到1个2,2个2,3个2的划分分案,就是图中的第二层节点。至此,就可以很清楚的知道S(N)的值就是求类似树的所有节点数,看图就知道S(N)是具有递归性质的,很明显的,S(N) = S(1) + S(2) +...+ S(N/2) + 1,最后的+1代表本身节点,初始值S(1) = 1,S(2) 。如果直接用递归算法简单求解的话会有很多重复计算,计算量太大,DP的基础。。呵呵。
这个公式作个简单的变形就OK了,如果N为奇数,S(N) =S(N-1);如果N为偶数,S(N) = S(N - 1) + S(N/2)。直接看代码吧。
#ifndef HEAD
#include <stdio.h>
#include <vector>
#include <math.h>
#include <string.h>
#include <string>
#include <iostream>
#include <queue>
#include <list>
#include <algorithm>
#include <stack>
#include <map>
using namespace std;
#endif // !HEAD
#define MODDDDDD 1000000000
long long sumset1[500001];
inline long long calcsumset(int N)
{
if (N == 1)
{
return 1;
}
else if (N==2)
{
return 2;
}
memset(sumset1, 0, sizeof(sumset1));
sumset1[1] = 1;
sumset1[2] = 2;
long long ires = 3;
int lasthalf = 1;
for (int i = 3; i <= N / 2;i++)
{
if (i / 2 == lasthalf)
sumset1[i] = sumset1[i - 1];
else
sumset1[i] = sumset1[i - 1] + sumset1[i / 2];
sumset1[i] %= MODDDDDD;
ires += sumset1[i];
ires %= MODDDDDD;
lasthalf = i / 2;
}
return (ires + 1) % MODDDDDD;
}
int main()
{
#ifdef _DEBUG
freopen("d:\\in.txt", "r", stdin);
#endif
int n;
scanf("%d\n", &n);
printf("%d\n", calcsumset(n));
return 0;
}