51nod-1201 整数划分

基准时间限制:1 秒 空间限制:131072 KB 分值: 80  难度:5级算法题
 收藏
 关注
将N分为若干个不同整数的和,有多少种不同的划分方式,例如:n = 6,{6} {1,5} {2,4} {1,2,3},共4种。由于数据较大,输出Mod 10^9 + 7的结果即可。
Input
输入1个数N(1 <= N <= 50000)。
Output
输出划分的数量Mod 10^9 + 7。
Input示例
6
Output示例
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;
} 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值