01背包问题
题目
有N件物品和一个容量为V的背包。第i件物品的费用是w[i],价值是v[i],求将哪些物品装入背包可使价值总和最大。
基本思路
01背包的特点有两个:一个是每件物品有且只有一个,二是每个物品可以选择放或者不放。
一般状态转移方程定义为:dp[i][j] = max ( dp[i-1][j],dp[i-1][j-w[i]]+v[i] )
这里的dp[i][j] = n表示前i件物品放入背包容量为j的背包的最大价值为n。
对于每个物品来说,都可以选择放进或者不放进背包。如果选择不放进背包,那么第i个件物品可有
可无,此时问题转换为"前i-1件物品放入容量为j的背包的最大价值";如果选择放进背包,那么此时问题
转换为"前i-1件物品放入容量为j-w[i]背包的最大价值"+v[i]。
回到正题
暴力法,穷举所有组合
int[] numOfOne;
int[] numOfZero;
String[] strs;
/*
对于strs中每个元素,都有选和不选两种选择
1) 选择该元素,那么我们手上的0和1相应的减少,但是我们的数量+1
2)不选择该元素,有两种情况
2.1 该元素需要的1和0,超出我们所拥有的1和0范围,此时跳过该元素
2.2 选了该元素太亏了,我们可以通过比较最后的数量来避免该情况
*/
public int findMaxForm(String[] strs, int m, int n) {
if(strs==null || strs.length==0)
return 0;
int len = strs.length;
this.numOfOne = new int[len];
this.numOfZero = new int[len];
this.strs = strs;
//统计strs中每个元素中0和1的个数
for(int i=0;i<len;i++){
numOfOne[i] = count(strs[i])[1];
numOfZero[i] = count(strs[i])[0];
}
return f(m,n,0);
}
//m个0 n个1
public int f(int m,int n,int index){
//遍历到头
if(strs.length==index)
return 0;
//当前字符串不能取 则跳过该字符串
if(n < numOfOne[index] || m<numOfZero[index])
return f(m,n,index+1);
//f(m-numOfZero[index],n-numOfOne[index],index+1)+1:选择当前字符,手上的0和1相应减少但数量+1
//f(m,n,index+1):不选择当前字符
return Math.max(f(m-numOfZero[index],n-numOfOne[index],index+1)+1,f(m,n,index+1));
}
//arr[0]:统计str中0的个数 存于arr[0]中
//arr[1]:统计str中1的个数 存于arr[1]中
public int[] count(String str){
int[] arr = new int[2];
for(int i=0;i<str.length();i++){
if(str.charAt(i) == '0')
arr[0] ++;
else if(str.charAt(i) == '1')
arr[1]++;
}
return arr;
}
暴力法一般不能appect,我们可以留意到strs数组中每个元素都有两种选择,这跟我们前面讨论的01背包很相似呀,这样我们可以按照01背包的思想,如果我们选择某个元素 strs[i] ,那么我们手上的0和1都会减少,但是我们拥有的数量会加一,我们用一个数组来保存某个时刻的数量,这样在比较的时候就不会出现重复计算了!!
动态规划优化
public int findMaxFormByDp(String[] strs, int m, int n){
if(strs==null || strs.length==0)
return 0;
//dp[i][j]:用i个0和j个1,最多能拼出dp[i][j]个字符串数目
int[][] dp = new int[m+1][n+1];
//每个数都轮流判断要不要放进背包
for(String s:strs){
//arr[0]:zero arr[1]:one
int[] arr = count(s);
//i个0 j个1
for(int i=m;i>=arr[0];i--){
for(int j=n;j>=arr[1];j--){
//判断要不要把这个数放进背包 如果放的话dp[i-arr[0]][j-arr[1]]+1
dp[i][j] = Math.max(dp[i][j],dp[i-arr[0]][j-arr[1]]+1);
}
}
}
//for(int[] arr:dp)
//System.out.println(Arrays.toString(arr));
return dp[m][n];
}
如果博主表述的有哪些不清晰欢迎大家在评论区指出哦!如果有帮助的话还请点赞!!谢谢大家