[ AtCoder Regular Contest 107 ] D - Number of Multisets(DP)

Problem

题目链接

Solution

  • 划分dp

原问题选 \(n\) 个数之和为 \(k\),其中每个数是 \(\frac{1}{2^i},i \in [0,\infty]\) 的方案数为 \(f(n,k)\)

求解划分dp一般抓住选 \(1\) 和不选 \(1\) 把原集合划分为两个集合。因此我们考虑:

  • \(1\) 这个集合可以表示为 \(f(n-1,k-1)\)

  • 不选 \(1\),那么这个问题即:\(n\) 个数之和为 \(K\),其中每个数是 \(\frac{1}{2^i},i \in [1,\infty]\) 的方案数。观察后发现,我们将该 \(n\) 个数全部乘 \(2\),那么等价于原问题限制:其中每个数是 \(\frac{1}{2^i},i \in [0,\infty]\)。所以该集合可以表示为 \(f(n,2k)\)

因此我们可以列出方程 \(f[n,k] = f[n-1,k-1] + f[n,2k]\)。边界 \(f[n,n] = 1, f[n,k]\)。边界 \(f[n,n] = 1, f[n,k] = 0(k>n)\)

Code

Talk is cheap.Show me the code.

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while(ch<'0' || ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<3)+(x<<1)+(ch^48); ch=getchar(); }
    return x * f;
}
const int N = 3007, mod = 998244353;
int n,K;
int f[N][N];
int DP(int i,int k) {
	if(k>i || k==0) return 0;	//先要判断是否越界,如果 k>i 可以会爆数组 f[N][N] 的下表,因为 N=3007 
	if(f[i][k] != -1) return f[i][k];
	if(i == k) return f[i][k] = 1;
	return f[i][k] = (DP(i-1,k-1) + DP(i,2*k)) % mod;
}
signed main()
{
	n = read(), K = read();
	memset(f, -1, sizeof(f));
	printf("%lld\n",DP(n,K));
	return 0;
}
/*
2525 425

687232272
*/

Summary

  • 划分dp抓住 选\(1\) 和不选 \(1\) 把原问题集合划分为两个集合。

Thanks

感谢 ovor大佬的题解Atcoder官方题解 让我搞懂了这道题。

posted @ 2020-11-02 15:52  基地AI  阅读(163)  评论(0编辑  收藏  举报