NYOJ 651 —— n划分为2个以上不同正整数的划分个数

Cut the rope

时间限制: 1000 ms  |  内存限制:65535 KB
描述

We have a rope whose length is L. We will cut the rope into two or more parts, the length of each part must be an integer, and no two parts have the same length.
  Your task is to calculate there exists how many results after the cutting. We say two results are different if and only if there is at least one part, which we can find in one result but can not find in the other result

输入
There is an integer T (1 <= T <= 50000) in the first line, which indicates there are T test cases in total.
  For each test case, there is only one integer L (1 <= L <= 50000) which has the same meaning as above.
输出
For each test case, you should output the correct answer of the above task in one line.
  Because the answer may be very large, you should just output the remainder of it divided by 1000000 instead
样例输入
3
2
3
6
样例输出
0
1
3

  这题对时间和空间的限制很高,所以,我们要尽力优化才行。

  一般的,求n划分为不同正整数的划分个数,这个问题可以通过dp来解决,定义dp[i][j]为i划分为不大于j的划分个数。但是,这里会遇到一个很棘手的问题:1 ≤ n ≤ 50000,所以如果按照这种状态的定义,空间、时间复杂度达到25*108,时间、空间都会超过限制!

  所以,一般的做法行不通。换一个思路:题目其实也是在求n划分为2、3、...个不同正整数的划分个数之和。

  求n划分为k个正整数的划分个数,这个问题也可以用dp来解决:定义dp[i][j]为i划分为j个不同正整数的划分个数。

  i的取值范围仍为[1, 50000],那么j的范围呢?即,i最多可以被划分为多少份呢?

  假设我们将i划分成了j份,那么在i取最小的情况下,i=1+2+...+j (划分的每个元素都要不相同) = (1+j)*j/2。因为i ≤ 50000,所以j最多取到316就已经足够了(316*317/2=50086>50000),而不可能分得更多了,再分就要出现相同的了(即使n=50000时,j=316也已经开始出现相同元素了)。

  故,j ≤ 316

  时间、空间大小降为316*50000=1.58*107,因为系数较小,可以AC

 

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
using namespace std;

const int MAXN = 50005;
const int MOD = 1000000;
int dp[MAXN][317];

void init()
{
    dp[1][1] = 1;
    for(int i=2; i<=316; i++)
        dp[1][i] = 0;
        
    for(int i=2; i<MAXN; i++) {
        for(int j=1; j<=316; j++) {
            if(j>=i)    dp[i][j] = 0;
            else    dp[i][j] = (dp[i-j][j-1] + dp[i-j][j])%MOD;
        }
    }
}

int main ()
{
    init();
    
    int T, n, ans;
    scanf("%d", &T);
    while(T--) {
        scanf("%d", &n);
        ans = 0;
        for(int i=2; i<317; i++)    ans = (ans + dp[n][i]) % MOD;
        printf("%d\n", ans);
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/AcIsFun/p/5348617.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值