自然数拆分(完全背包)

 

自然数拆分(完全背包)————PTA

一、题目:

给定一个自然数N,要求把N拆分成若干个正整数相加的形式,参与加法运算的数可以重复。与“自然数拆分问题”类似,同样需要满足方案的不重复。

所谓拆分方式的重复性判定如下:给定N=a​1​​+a​2​​+...a​m1​​ 和 N=b​1​​+b​2​​+...b​m2​​ 表示整数N的两种拆分方式。对于∀a​i​​,b​j​​≥1,令集合A={a​i​​∣1≤i≤m​1​​},B={b​j​​∣1≤j≤m​2​​}。若满足集合A=B,则称这两种拆分方式是重复的。

例如 6 = 3 + 2 和 6 = 2 + 3, 就是重复的拆分方式。

现在需要你求满足条件的拆分有多少种?

输入格式:

第一行自然整数T,表示之后测试数据组数, 以后T行,每行一个自然数N,(1<N<=4000)

注意:本题规模为4000,回溯法还合适吗?仔细思考应该如何设计算法

建议自行学习完全背包相关知识

输出格式:

T行,每行输出一个整数,表示拆分的方案数,结果对2147483648取模。

输入样例:

在这里给出一组输入。例如:

2

6

7

输出样例:

在这里给出相应的输出。例如:

11

15

二、思路:

自然数拆分问题等价于完全背包问题:将n个物品放入背包(容量为n)的方案数

(1)f[i][j]表示:从前i个数中选取,使得结果刚好为j

(2)f[i][j] = f[i-1][j](第i个物品选择0次)+ f[i-1][j-vi](第i个物品选择1次) + f[i-1][j-2vi](第i个物品选择2次)+...+ f[i-1][j-mvi](第i个物品选择m次,m=j/vi)

    用j-vi代替j:

         f[i][j-vi] = f[i-1][j-vi] + f[i-1][j-2vi] + ...+ f[i-1][j-mvi]

两式相减得:f[i][j] = f[i-1][j]+f[i][j-vi]

背包剩余容量j只与f[][]的第二维有关,f[j] = f[j] +f[j-vi](vi= =i),推出:f[j]+=f[j-i]

利用动态规划策略,建立一张存放中间结果的表,提高效率

 

三、附上代码:

#include <stdio.h>
#include<string.h>
void division_sum(int n, long long dp[])
{
    int  i,j;
    for(i=1;i<=n;i++)
        for(j=i;j<=n;j++)
            dp[j] = (dp[j] + dp[j-i])%2147483648;
}
int main()
{
    int T, n[4001],i;
    long long dp[4001];
    scanf("%d", &T);
    memset(dp,0,sizeof(4001));
    dp[0] = 1;
    int max = 0;
    for (i = 1; i <= T; i++)
    {
        scanf("%d", &n[i]);
        if (n[i] > max)
            max = n[i];
    }
    division_sum(max, dp);
    for (i = 1; i <= T; i++)
        printf("%d\n", dp[n[i]]);
    return 0;
}

如有错误,欢迎指正!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值