从01背包学习贪心算法和动态规划:
算法的思路其实很大程度上都是相通的,比如在提升算法运行时间的不断探索中,我们用分治的思想来将一个大问题分解为很多小问题进行求解,并且这些子问题与原问题的结构是一样的,比如归并排序,比如第i层是排四个数,第i+1层则是排八个数,问题的规模发生变化但结构不变。而动态规划则是沿用的分治的思想,但是比分治多两个必要条件:重叠子问题和最优子结构。前者要求问题空间要足够小,得到的递归算法会反复求解相同的子问题,而不是产生新的子问题,比如LCS和01背包;而后者则比较晦涩,具体来说就是如果要找问题的最优解先确定该问题最优解包含了其子问题最优解,这样就把责任一层层推下去(在程序中体现在直到遇见哨兵为止)。二贪心算法则又是延续动态规划,因为动态规划自底向上计算总是要维护一个二维数组记录所有的子问题解,对于有些优化问题,没有必要都计算。这时候我们可以使用自上而下的策略,做出一个选择,然后求剩下的子问题,如果面临两个选择,则选择一个,继续求解该子问题即可。
那么接下来我们用01背包问题具体说明这两种算法:
问题描述:
有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。基本思路这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。用子问题定义状态:即f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。则其状态转移方程便是:
https://img-blog.csdn.net/20150501212932504
package DP;
import java.awt.*;
import java.util.Scanner;
public class bagdp {
static final int NIF = 9999;
static int N;//背包数目
static int[] worth;//单个物品价值
static int[] cap;//单个物品容量
static int[][] values;//存放价值,也是自上而下求解问题的必备数据结构
static int maxVal;//最大价值
public static void main(String[] args){
Scanner in = new Scanner(System.in);
maxVal = in.nextInt();
N = in.nextInt();
worth = new int[N+1];
cap = new int[N+1];
values = new int[N+1][maxVal+1];
for(int i = 1;i <= N;i++){
worth[i] = in.nextInt();
cap[i] = in.nextInt();
}
for(int i = 0;i <= N;i++)
for(int j = 0;j <= maxVal;j++)
values[i][j] = NIF;
for(int i = 0;i <= N;i++)
values[i][0] = 0;
for(int i = 0;i <= maxVal;i++)
values[0][i] = 0;
//System.out.print("可装最大价值为:"+greedy(N,maxVal)+"\n");
System.out.print("可装最大价值为:"+greedy2()+"\n");
printBags();
in.close();
}
public static int greedy(int i,int j){//贪心算法
if(values[i][j] < NIF) return values[i][j];//备忘录
if(j < cap[i]) values[i][j] = greedy(i-1,j);//容不下就不要了
if(j >= cap[i]){//可以容得下
values[i][j] = max(greedy(i-1,j),greedy(i-1,j-cap[i])+worth[i]);
}
return values[i][j];
}
public static int greedy2(){//动态规划算法,自下而上
for(int i = 1;i <= N;i++)
for(int j = 1;j <= maxVal;j++)
{
if(j < cap[i]) values[i][j] = values[i-1][j];
else if(j >= cap[i]){
values[i][j] = max(values[i-1][j],values[i-1][j-cap[i]]+worth[i]);
}
}
return values[N][maxVal];
}
public static int max(int a,int b){
return a>b?a:b;
}
public static void printBags(){//回溯打印选择的背包
int j = maxVal;
for(int i = N;i > 0;i--){
if(values[i][j] > values[i-1][j]){
System.out.print("背包"+i+" ");
j = j -cap[i];
if(j < 0) break;
}
}
}
}
greedy是贪心算法(采用了备忘录自上而下的思想),greedy2则采用动态规划思想。