51nod-1259 整数划分 V2

基准时间限制:1 秒 空间限制:131072 KB 分值: 80  难度:5级算法题
 收藏
 关注
将N分为若干个整数的和,有多少种不同的划分方式,例如:n = 4,{4}  {1,3}  {2,2}  {1,1,2} {1,1,1,1},共5种。由于数据较大,输出Mod 10^9 + 7的结果即可。
Input
输入1个数N(1 <= N <= 50000)。
Output
输出划分的数量Mod 10^9 + 7。
Input示例
4
Output示例
5
题解:直接暴力背包,复杂度O(n^2),妥妥tle。
使用分块背包,将背包分成两部分,第一部分是[1,sqrt(n) ),用完全背包的方法暴力得出所有解;

第二部分是[(sqrt(n)), n],这一部分的背包每个值做多取sqrt(n)次,我们设dp[i][j]为和为i,使用了j个数的背包。那么问题在于如果找到转移方程;要到达dp[i][j]这一状态且不重复,首先我们可以向其中加入任意一个新的数,也就是从dp[x][j - 1]到达dp[i][j];其次,我们可以不加新的数,只改变当前数组里面的值,也就是从dp[y][j]到达dp[i][j];

如何实现上面的操作?首先我们规定每次只能向背包中加入大小为sqrt(n)的物品,即x = i - sqrt(n);为了使得从[sqrt(n), n]的所有物品在背包中都能使用,每次j不增加时当前背包里的数全加1(保证有序,进而保证不重复)也就是y = i - j;

故转移方程为f[i][j] = f[i - j][j] + f[i - sqrt(n)][j - 1];

最后使用乘法累乘即可, ans = ∑(0, n)(dp[i] * dp1[n - 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[maxn] = {0}, dp1[maxn] = {0}, f[maxn][300] = {0};

int main(){
	ll n, m;
	scanf("%lld", &n);
	m = sqrt(n) + 1;
	dp[0] = dp1[0] = 1;
	for(ll i = 1; i < m; i++){
		for(ll j = 0; j + i <= n; j++){
			dp[j + i] += dp[j];
			dp[j + i] %= mod;
		}
	}
	f[m][1] = 1;
	for(ll i = m; i <= n; i++){
		for(ll j = 1; j <= m; j++){
			f[i][j] = (f[i][j] + f[i - m][j - 1]) % mod;
			f[i][j] = (f[i][j] + f[i - j][j]) % mod;
			dp1[i] = (dp1[i] + f[i][j]) % mod;
		}
	}
	ll ans = 0;
	for(ll i = 0; i <= n ; i++)
		ans = (ans + dp1[i] * dp[n - i]) % mod;
	printf("%lld\n", ans);
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/qq_37064135/article/details/80094721