Sumsets (dp, 找规律,找递推式)

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). 
Input
A single line with a single integer, N.
Output
The number of ways to represent N as the indicated sum. Due to the potential huge size of this number, print only last 9 digits (in base 10 representation).
Sample Input
7
Sample 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;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值