01背包问题
i:前i个元素
j:当前可用空间
A[i]:第i个物品的空间大小
dp[i,j]:表示在i个元素中选取的总空间等于j的方案数量
解法一:二维数组+动态规划
状态转移方程:
1. 不选i:dp[i][j] = dp[i-1][j]
2. 选i:dp[i][j] = dp[i-1][j-A[i]]
所以总的方案数就1和2的和
注意:因为体积为0时,即j=0时,是存在一种方案的,所以dp[0][0] = 1
public class Main{
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int m = scan.nextInt();
int[] a = new int[1005];
int[][] dp = new int[1005][1005];
for(int i=1; i<=n; i++){
a[i] = scan.nextInt();
}
dp[0][0] = 1;
for(int i=1; i<=n; i++){
for(int j=0; j<=m; j++){
dp[i][j] = dp[i-1][j];
if(j >= a[i]){
dp[i][j] += dp[i-1][j-a[i]];
}
}
}
System.out.println(dp[n][m]);
}
}
解法二:一位数组+动态规划
因为在解法一中,状态转移方程只使用到了i-1和j-a[i],所以对于二维数组来说,其他记录的状态都是多余的,所以我们可以使用滚动数组来对解法一进行优化
状态转移方程:dp[j] += dp[j-a[i]]
注意:当变为dp[j] += dp[j-a[i]]
后,对应的二维状态转移方程为:dp[j][j] += dp[j][j-a[i]]
和原二维转移方程矛盾,因为在顺序遍历过程中会导致i-1层的数据先被覆盖,所以需要逆序遍历,这样就会先计算高层元素而不会影响底层元素
public class Main{
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int m = scan.nextInt();
int[] a = new int[1005];
int[] dp = new int[1005];
for(int i=1; i<=n; i++){
a[i] = scan.nextInt();
}
dp[0] = 1;
for(int i=1; i<=n; i++){
for(int j=m; j>=a[i]; j--){
dp[j] += dp[j-a[i]];
}
}
System.out.println(dp[m]);
}
}
二维的dp数组的列给小了,起码得10001才行
感谢
请问朴素写法的j为什么要从0开始
个人理解,权当给同学们一个靶子,抛砖引玉吧:
其实为什么j要从0开始,是一种代码变形的结果,因为大家不知道原始的形态是什么样的,所以有困惑,原始形态是这样的:
为什么要用循环初始化掉左侧第一列呢?因为使用瞪眼大法知,每一个状态,都是从上一行的前序状态转移而来,而递推值开始不能是0(原因可以参考下面咸鱼兄弟问题我的回答),只能是1,也就是左侧第一列全要给初始值1.之所以现在写成j=0,其实是把原始形态的代码进行了等价变形,这样代码更短,当然,如果你不习惯这样写,按我的写法也是一样的。算法这东西,细节在搞懂之后,就需要背诵一些内容了,背的时间长了,随手就可以写出来,写出来也就对了,代码还短。但必须要搞清楚细节,否则背诵下来是没有灵魂的~
因为f[i][0]需要计算,以推出后面的状态,如果不从0开始,那么默认值是0显然不对(从前i个物品选,体积恰好等于j的方案就是1个,即什么都不选)
请问为什么f[0][0]要等于1呢
从前0个数中选,且总和不超过0,显然仅有一种方案即什么都不选,而f[i][j]表示的是方案的数目,故f[0[0] = 1;
使用瞪眼大法(观察法)观察状态转移方程可知,一个新状态$f[i][j]$,要么从$f[i - 1][j]$转移过来,要么从$f[i - 1][j - a[i]]$转移而来,而我们定义的属性是方案数,所以采用了累加的办法就可以了。这样从大到小去思考,是不是就是大的依赖小的,小的依赖更小的,那么最小的就是$f[0][0]$,如果它是$0$,那么,我们就会发现一个很滑稽的结论,$0$不管怎么累加都是$0$,最后就加了个寂寞~,似乎与乘法世界的本原是$1$概念是一样的,比如我们要计算累乘积,一定是定义初始值$s=1$,然后再
s=*2,s=*3,s*=4
,你把$s$定义成$0$试试,也是乘了个寂寞。哈哈好的蟹蟹
好的 蟹蟹
二维的,他这应该写错了
没错
二维的为什么j不能从1开始?
二维的为什么j不能从1开始?
有点小问题,应该f[i][0]都初始化为1才对, 我试了,不然ac不了
#include[HTML_REMOVED]
#include[HTML_REMOVED]
#include[HTML_REMOVED]
#include[HTML_REMOVED]
#include[HTML_REMOVED]
#include[HTML_REMOVED]
#include[HTML_REMOVED]
#include[HTML_REMOVED]
#include[HTML_REMOVED]
#include[HTML_REMOVED]
#include[HTML_REMOVED]
#include[HTML_REMOVED]
#include[HTML_REMOVED]
#include[HTML_REMOVED]
#include[HTML_REMOVED]
using namespace std;
const int N = 10010;
int f[110][N];
int v[110];
int n, m;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
}
加不加无所谓吧,都可以过
呜呜呜终于懂了二维到一维的转换了,谢谢大佬QAQ
给出二维的属实对我们菜鸡贴心