算法练习 (8) 01背包问题

题目描述

给定一个有n个正整数的数组A和一个整数sum,求选择数组A中部分数字和为sum的方案数。

当两种选取方案有一个数字的下标不一样,我们就认为是不同的组成方案。


样例

输入描述:

输入为两行:
 第一行为两个正整数n(1 ≤ n ≤ 1000),sum(1 ≤ sum ≤ 1000)
 第二行为n个正整数A[i](32位整数),以空格隔开。

输出描述:

输出所求的方案数
示例1

5 15 5 5 10 2 3

输出

4

思路分析

01背包的状态转移方程 f[i,j] = max{f[i-1,j-wi] + p[i](j>=wi), f[i-1,j]}

f[i,j]表示在前i件物品中,选择若干件放在承重为j的背包中,可以取得的最大价值,pi表示第i件物品的价值,wi表示第i件物品的重量

此题中,无物体重量w的概念,而sum可以看做物品价值

列出表格如下


做法

1.由于每个数总能把0填上,且0不可填上初0外其余数,所以数组第一行全填0,第 一列全填1;
2.从第二行第二列开始遍历数组。如果列所在数字减去该行数小于0(即j < value[i]),那么该格子继承本列上一行的数字。例如图中(2,10)对应格子。由于让10得到2,那必须由-8+5得到,但是该题无法得到比0小的数,因此由10之前的数得到2的最多可能与他之前的数(即5)是一样的;
3.如果列所在数字减去该行数大于等于0( j >= value[i]),该格子内容为该列上一行数字与上一行差值所在格子数量和。什么意思呢?例如(10,3),若想要用3之前的数列得到10,除了 它上一行(即2)本身就能得到2个10外,只要之前的数字是7,7+3依然可以得到10。因此去看上一行中列数为7的格子数值,为2,即它上一个数有2中组合得到7,7+3=10。那该行数值即为2+2=4。

代码

public static void main(String[] args)
	{
		Scanner s = new Scanner(System.in);
		int n = s.nextInt();
		int sum = s.nextInt();
		int value[] = new int[n];
		long dp[] = new long[sum+1];
		dp[0] = 1;
		for (int i = 0; i < n; i++) {
            value[i] = s.nextInt();
            for (int j = sum; j >= 0; j--) {
                if (j >= value[i]) {
                    dp[j] = dp[j] + dp[j - value[i]];
                }
            }
        }
        System.out.println(dp[sum]);
	}

结果

猜你喜欢

转载自blog.csdn.net/sun10081/article/details/80880093