【POJ】剑指DP:2229.Sumsets

0.概述

输入一个数字n,将其分解为2的幂次之和,求所有的分解方法。

1.分析

找到递推关系特别简单,但是我没找出来,所以卡住了,丢人。
为什么没找出来呢?
因为我就会最简单的DP,最简单的DP一般都是这种套路:有一个一维或者二维的DP表,我们现在想求DP[n],我们已经有DP[n-1],那么就想想怎么把这俩联系起来——我本来以为这题也能这么做,但是这题最重要的要求是分解方式不看顺序,也就是说1,1,2和1,2,1算一种,那么最简单的DP方法就用不了了。
对于有重复的DP,需要在求DP[n]的时候,把情况分开,比如说分成两类,这两类的分解方法没有重复;如果我们在最开始就把类别区分好,那么之后有重复,也没关系。这是其一。其二就是观察样例找规律:

  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
    这是题中给出的例子,我们可以发现,最开始的1全删掉,就是n=6时候的结果。这可很好玩,这意味着DP[n]在n为奇数的时候和n-1时候的情况一样,“全加一和全不加一的分解方法数相同”。
    那么推广一下,“全乘二和全不乘二的分解方法一样”。这句话很重要。上面我们看完了奇数,然后来看偶数:对一个偶数来说,我们同样要在最开始把情况分开:分成“分解方式中有没有1”,有1,那必定有两个1;没有1,那么最小是2。
    对于有两个1的情况,“全减二和全不减二的分解方法数相同”,那么DP[n]中“存在俩1”的情况数量就和DP[n-2]相同,对于不含1的情况,“全除以二和全不除以二的分解方法数相同”,那么DP[n]中“最小是2”的情况数量就和DP[n/2]相同。所以递推关系就知道了。
    DP[n]=DP[n-1] or DP[n-2]+DP[n/2]

2.总结

对于需要考虑是否重复的问题,要在刚开始就把情况分开,保证“确保不同”在最初,之后就不用头痛后续是不是重复了。

PS:代码如下:

#include<iostream>
#include<vector>
#include<string>
#include<iterator>
#include<queue>
#include<list>
#include<set>
using namespace std;
int main()
{
	int n;
	cin >> n;
	vector<int> v=vector<int>(n+1,0);
	v[1] = 1;
	v[2] = 2;
	for (int i = 3; i <= n; ++i)
	{
		if (i % 2 == 1)
			v[i] = v[i - 1];
		else
			v[i] = (v[i - 2] + v[i / 2]) % 1000000000;
	}
	cout<<v[n];
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值