适用问题
贪心选择:
整体的最优解可通过一系列局部最优解达到。每次的选择可以依赖以前做出的选择,但不能依赖后面的选择。
最优子结构:
问题的整体最优解中包含着他的子问题的最优解。
贪心算法和动态规划的区别
动态规划:
每步所做的选择往往依赖于相关子问题的解。通常以自底向上的方式解各子问题。
贪心算法:
以迭代的方式做出相继的贪心选择,每做一次贪心选择就将所求的问题简化为规模更小的子问题。通常以自顶向下的方式进行。
活动安排问题
问题描述:
有若干个活动,第i个开始时间和结束时间是[Si,fi),只有一个活动场地可以利用,活动之间不能交叠,求最多安排多少个活动?
思路:
将活动的结束时间由小到大排序,安排时使剩余的可安排时间段极大化,以便安排尽可能多的相容活动。
代码:
public class activitiesArrange {
public static int greedySelector(int[] s, int[] f, boolean[] a) {
int n = s.length - 1;
//安排第一个活动,标记为true
a[1] = true;
int j = 1;
int count = 1;
for (int i = 2; i <= n; i++) {
//检验下一个活动的开始时间是否晚于当前活动的结束时间
if (s[i] >= f[j]) {
//如果晚于,则表示两个活动相互兼容,将活动标记为true
a[i] = true;
j = i;
//记已经安排活动的个数
count++;
} else {
//与已安排活动不兼容,标记此活动未安排
a[i] = false;
}
}
return count;
}
public static void main(String[] args) {
//初始化数据s数组记录活动开始时间;f数组记录活动结束时间(已经提前按照结束时间由小到大排好序)
int[] s = { 1, 3, 0, 5, 3, 5, 6, 8, 8, 2, 12 };
int[] f = { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };
//声明一个boolean型数组
boolean[] a = new boolean[s.length]; //记录是否选择了该活动
int result = greedySelector(s, f, a);
System.out.println("Result is: " + result);
for (int i = 1; i <= s.length - 1; i++) {
if (a[i]) {
System.out.println("第" + i + "活动被选中,其开始时间为:" + s[i] + ",结束时间为:" + f[i]);
}
}
}
}
一般背包
与0-1背包问题类似,所不同的是在选择物品i装入背包时,可以选择物品i的一部分,而不一定要全部装入背包。
问题:
有N件物品和一个容量为V的背包。第i件物品的重量是w[i],价值是v[i]。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。
思路:
首先,计算每种物品单位重量的价值Vi/Wi ,然后,依贪心选择策略,将尽可能多的单位重量价值最高的物品装入背包(若将这种物品全部装入背包后,背包内的物品总重量未超过C,则选择单位重量价值次高的物品并尽可能多地装入背包)。依此策略一直地进行下去,直到背包装满为止。
代码:
//一般背包问题
import java.util.Arrays;
/**
* 贪心算法--->背包问题
* 假设有一个背包最大可以装 150kg 物品
* 某物品 重量 价值(元) 性价比(单位重量的价格)
* a 35kg 10 10/35
* b 30kg 40 40/30
* c 60kg 30 30/60
* d 50kg 50 50/50
* e 40kg 35 35/40
* f 10kg 40 40/10
* g 25kg 30 30/25
*
* 求:背包可以装入的最大价值?
*/
public class knapsack {
private static int capacity=152; //背包最大容量
private static int [] goods_weights=new int [] {35,30,60,50,40,10,25}; //各个物品的重量
private static int [] goods_values=new int[] {10,40,30,50,35,40,30}; //各个物品的价值
/**
* @param capacity 背包容量
* @param weights 各个物品的重量
* @param values 各个物品的价值
*/
private void knapsackGreedy(int capacity,int weights[],int values[]) {
int n=weights.length; //物品的数量
Double[] r=new Double[n]; //保存性价比的数组
int [] index=new int[n]; //保存按性价比排序的物品的下标
//计算得到各个物品的性价比
for (int i = 0; i < n; i++) {
r[i]=(double)values[i]/weights[i];
index[i]=i; //初始化各个物品的默认性价比排序
}
//对各个物品的性价比进行排序
for(int i=0;i<r.length-1;i++) {
for(int j=i+1;j<r.length;j++) {
if(r[i]<r[j]) {
double temp=r[i];
r[i]=r[j];
r[j]=temp;
//将排序后性价比的下标更新为性价比排序后的位置
int x=index[i];
index[i]=index[j];
index[j]=x;
}
}
}
//将排序好的重量和价值分别保存到数组
int [] w1=new int[n];
int [] v1=new int[n];
for(int i=0;i<n;i++) {
w1[i]=weights[index[i]];
v1[i]=values[index[i]];
}
//将物品装入背包
//记录哪些物品已经被装入背包 0 没有装入背包 1 代表已经装入背包
int [] x=new int[n];
double maxValue=0;
for(int i=0;i<n;i++) {
if(w1[i]<=capacity) {
//还可以装的下
x[i]=1; //表示将该物品装入背包
System.out.println("物品:"+w1[i]+" 被放进了");
maxValue+=v1[i];
capacity-=w1[i];
}else{
System.out.println("物品:"+w1[i]+" 被放进了一部分");
x[i]=2; //表示将该物品的一部分装入背包
maxValue = (double)capacity+r[i]*(double)capacity;
break;
}
}
System.out.println("总共放下的物品的数量为:"+Arrays.toString(x));
System.out.println("最大价值为:"+maxValue);
}
public static void main(String[] args) {
knapsack k=new knapsack();
k.knapsackGreedy(capacity, goods_weights,
goods_values);
}
}