自然数拆分(完全背包)————PTA
一、题目:
给定一个自然数N,要求把N拆分成若干个正整数相加的形式,参与加法运算的数可以重复。与“自然数拆分问题”类似,同样需要满足方案的不重复。
所谓拆分方式的重复性判定如下:给定N=a1+a2+...am1 和 N=b1+b2+...bm2 表示整数N的两种拆分方式。对于∀ai,bj≥1,令集合A={ai∣1≤i≤m1},B={bj∣1≤j≤m2}。若满足集合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;
}