输入1个数N(1 <= N <= 50000)。
输出划分的数量Mod 10^9 + 7。
6
4
题解:设dp[j][i]为取了前j个数,和为i的方案。要到达dp[j][i],有两种方案:
第一种方案不增加新元素,即从dp[j][x]到达dp[j][i],为了保证不重复,故每个元素+1,即x = i - j;
第二种方案增加新元素,即从dp[j - 1][x]到达dp[j][i],为了保证dp[j - 1][x]与dp[j][x]不重复,我们发现dp[j][x]的元素都大1,故dp[j - 1][x]的新增元素为1,即x = i - j;
故转移方程为:dp[j][i] = dp[j][i - j] + dp[j - 1][i - j]。此种方法时间空间复杂度都为O(n^2),超时超内存!
将n个数分成两部分[0, (sqrt(n) + 0.9)),[(sqrt(n) + 0.9), n];
对于第一部分使用01背部。时间为O(n * (sqrt(n) + 0.9));
对于第二部分,每个元素至多取(sqrt(n) + 0.9)次,我们定义第一位是取j个元素,故我们可以使用转移方程:dp[j][i] = dp[j][i - j] + dp[j - 1][i - j];注意,因为新增元素不是1而是(sqrt(n) + 0.9),故需要改变转移方程,
最终的方程为:dp[j][i] = dp[j][i - j] + dp[j - 1][i - j - (sqrt(n) + 0.9) + 1];复杂度也为O(n * (sqrt(n) + 0.9));记录一个f1数组,
f1[i] = ∑(1<=x<=(sqrt(n) + 0.9))dp[x][i];
最终乘起来即可!
AC代码
#include <stdio.h>
#include <iostream>
#include <string>
#include <queue>
#include <map>
#include <vector>
#include <algorithm>
#include <string.h>
#include <cmath>
typedef long long ll;
using namespace std;
const ll maxn = 55555, mod = 1e9 + 7;
ll dp[244][maxn] = {0}, f[maxn] = {0}, f1[maxn] = {0};
int main(){
ll n;
scanf("%lld", &n);
ll m = int(double(sqrt(n) + 0.9));
f[0] = f1[0] = 1;
for(ll i = 1; i < m; i++){
for(ll j = n; j - i >= 0; j--)
f[j] = (f[j] + f[j - i]) % mod;
}
dp[1][m] = 1;
for(ll i = m; i <= n; i++){
for(ll j = 1; j <= m; j++){
dp[j][i] = (dp[j][i - j] + dp[j][i]) % mod;
if(i - m - j + 1 >= 0)
dp[j][i] = (dp[j][i] + dp[j - 1][i - m - j + 1]) % mod;
f1[i] = (f1[i] + dp[j][i]) % mod;
}
}
ll ans = 0;
for(ll i = 0; i <= n; i++)
ans = (ans + f1[i] * f[n - i]) % mod;
printf("%lld\n", ans);
return 0;
}