POJ2229---Sumsets(数的划分,dp,透彻地讲解两种递推方法)

题意:用1,2,4,8,16,32,~~~ 2^n 。 去组合一个数。
方法①:
dp[i]:i的划分数。
dp[i]=dp[i-1]                //i为奇数
dp[i]=dp[i-1]+dp[i/2]    //i为偶数
边界控制:dp[1]=1


当i为奇数
很好理解,奇数是1产生的吧,每个划分里面必须都要有1,把这个1减去,就是偶数i-1的划分。把7的每个划分都减去1不就是6的吗?
i=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


当i为偶数,把所有的划分 分成两组,带1的和不带1的。带1的不又是比原偶数(6)小1的奇数(5)的划分了吗?不带1的划分里面肯定含有2吧?同时除2,不又产生1了吗(也就是说,又是一个完整的划分了)?
i=6
1) 1+1+1+1+1+1
2) 1+1+1+1+2
3) 1+1+2+2
4) 1+1+4
5) 2+2+2    (1+1+1)
6) 2+4        (1+2)不就是3的所有(完整性)划分了吗?

总之,这种递推关系式一定要紧扣定义去递推!!!推出来的子问题不能有重合,更需要是一个完整的子问题!




方法2:
dp[i][j]:只用前i个数,j的组合数

dp[i][j]=∑dp[i-1][j-k*w[i]]   k>=0;
含义:先从前i-1个数里面组合成j-k*w[i].再从第i个里面拿出来k个。有点完全背包的意思。
化简:
原式=∑dp[i-1][j-k*w[i]]                      k>=0;
    =dp[i-1][j]+∑dp[i-1][j-k*w[i]]           k>=1;
    =dp[i-1][j]+∑dp[i-1][j-w[i]-(k-1)*w[i]]  k-1>=0;
    =dp[i-1][j]+dp[i][j-w[i]]

dp[i][j]=dp[i-1][j]+dp[i][j-w[i]]

边界控制dp[1][j]=1;

可以利用一维数组,但是注意第二个循环要顺序走。



AC代码1:

#include<iostream>
#include<cstdio>
#include<algorithm>

using namespace std;

long long int dp[1000010];
const long long MOD = (int)1e9;
int main()
{
	int N; 
	while (cin >> N)
	{
		dp[1] = 1;
		for (int i = 2; i <= N; i++)
		{
			if (i & 1)dp[i] = dp[i - 1];
			else dp[i] =( dp[i - 1] + dp[i / 2]) % MOD;
		}
		cout << dp[N]<< endl;
	}
	system("pause");
}


AC代码2:

#include<iostream>
#include<cstdio>
#include<algorithm>

using namespace std;
long w[21];//2的20次方刚好比最大数据多一点
int dp[1000005];
const long long MOD = (int)1e9;
int main()
{
	int N;	
	w[1] = 1;
	for (int i = 2; i <= 20; i++)  //20个物品,最后一个为2^19;
		w[i] = w[i - 1] * 2;
	while( cin >> N)
	{
	fill(dp, dp + N + 1, 1);
	for (int i = 2; i <= 20; i++)
		for (int j = w[i]; j<=N; j++)
		{
			dp[j] += dp[j - w[i]];
			dp[j] %= MOD;
		}
	cout << dp[N] << endl;
	}
	system("pause");
}




  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值