一道凑数问题

xiaoxiao2021-02-28  23

Problem Description

题目来源:http://blog.csdn.net/ljhandlwt/article/details/75014415 给定n,给定池,求池的子集的和为n的个数。池为2^0,2^0,2^1,2^1…包含任意2的次幂,且每个数都有两份。

Solution

对于任意2^i,有2^i=2^i=2^(i-1)*2=sum(2^(i-j))+2^(i-j-1)*2 其形式为连续的2^次幂累加及末尾最低项乘2。 题目表示任意2的次幂有两份。 因此若n=2^N+2^M,且N<M, 则我们可以认为,2^M只能来自于2^N,2^N+1,.....2^M。 (更低位中一定存在一位已被2^N使用了两次。特殊的,若2^N只使用了自己,2^M可以使用更低位,但其方案与2^N使用更低位是重复的) 将n按二进制分解,有n=2^a1+2^a2+....2^am, a1<a2<...<am 则可以dpf[i]表示凑到2^ai时的方案数。 对于凑2^a[i],方案总共a[i]-a[i-1]+1种。 这里的一个问题是2^a[i-1]可能在f[i-1]用到过,所以需要容斥一下。 2^a[i-1]是凑2^a[i-1]的方案一种,其对于f[i-1]的总贡献就是f[i-2]。 此时,对于f[i],占用2^a[i-1]的那一种方案不可用,其他可用。 同理,f[i-1]的剩余部分可以使用f[i]的所有方案。 所以有: f[i]=(f[i-1]-f[i-2])*(a[i]-a[i-1]+1)+f[i-2]*(a[i]-a[i-1]) 即 f[i]=f[i-1]*(a[i]-a[i-1]+1)-f[i-2] PS:对于本题,用f[i][0/1]表示凑到第i个数时,用或者不用自己来凑,这样的DP方式可能更自然。因为笔者智商极低,使用这种二维DP做完后才发现可以化简成一维,而后强行思考,才得出了这种容斥思路。

Code

#include <cstdio> using namespace std; int a[66],n,f[66]; int main() { int N; while (scanf("%d",&N)!=EOF) { int i=0; n=0; while (N) { if (N%2) a[++n]=i; N/=2; i++; } f[0]=1; f[1]=a[1]+1; for (i=2;i<=n;i++) f[i]=f[i-1]*(a[i]-a[i-1]+1)-f[i-2]; printf("%d\n",f[n]); } }
转载请注明原文地址: https://www.6miu.com/read-2350226.html

最新回复(0)