掌握贪心算法求解问题的思想;针对不同的问题,会利用贪心算法进行问题拆分和求解以及时间复杂度分析,并利用JAVA/C/C++等编程语言将算法转换为对应的程序上机运行(语言自选)。
理解装载问题及背包问题的贪心求解策略;对比分析与动态规划求解最优时算法异同;能够根据贪心算法,进行装载问题及背包问题最优规则选取,并编码实现。
②基于贪心算法设计装载问题的求解;
针对指定容量C的船,给定n个集装箱,各集装箱重量为wi。如何将集装箱装到船上保证装的集装个数最多?
③基于贪心算法设计背包问题的求解;
针对指定容量C的背包,给定n个物品,各物品具有重量wi、价值vi。如何选择物品装入背包,使得价值最大(未要求0/1性)?
④对上述算法进行时间复杂性分析,并输出程序运行时间及运行结果。
实验原理:
1、针对装载问题,如何利用贪心算法进行算法设计
分析问题:
求解的最优装载问题,要求装载的物品的数量尽可能多,而船的容量是固定的,那么优先把重量小的物品放进去,在容量固定的情况下,装的物品最多。采用重量最轻者先装的贪心选择策略,从局部最优达到全局最优,从而产生最优装载问题的最优解。
建模及约束条件:
算法思路及策略:
先将集装箱依其重量从大到小排序,(x1,x2.......xn)是最优装载问题的一个最优解,又设k=min{i|xi=1}{ 1<=i<=n}.如果给定的最优装载问题有解,则1<=k<=n;然后选择最轻的待选物品,给出选择第一个物品是最轻的,如果第一个集装箱的重量就大于最大承重则后面的均无法装入。选完第一个物品,剩下的也是在排好序中的物品选择最轻的,这就是最优子结构。最后一个一个求出最优子结构将他们合并起来,求出装载最多的一个解。在最优子结构的综合就是装载问题的最优解。我还加入一个index记录集装箱的序号,求出加入的集装箱序号。
算法精选:
2、背包问题,贪心算法设计求解策略
算法思路及策略:
三种策略如下:
1、先把价值高的装进去,这样来说,能够确保背包内物品价值的有效增长,但是背包容量消耗太快,不利于总体价值的最大化。
2、先把重量小的物品装进去,这种策略能确保背包内的空间得以有效的利用,但是背包内物品的价值却不能有效增长,即增长缓慢,也不利于总体价值的最大化。
3、以上两种策略只考虑到了背包价值的增长和背包容量的利用,都不利于背包内物品价值的最大化。不妨,给每件物品赋予权重=价值/重量,并且按照物品的权重降序排序,依次将物品装入背包。
我们选第三种是最好的贪心策略,收益最高。先选取比值最大的物品。比较其重量是否小于背包容量,如果小,就放入,如果大于背包容量,就对其进行切割。循环第一步,直到背包容量等于0。
求解步骤:
首先计算每种物品单位重量的价值v[i]/w[i],然后,依贪心选择策略,将尽可能多的单位重量价值最高的物品装入背包。若将这种物品全部装入背包后,背包内的物品总重量未超过C,则选择单位重量价值次高的物品并尽可能多地装入背包。依此策略一直地进行下去,直到背包装满为止。
背包问题与0-1背包问题的区别:
具有最优子结构性质和贪心选择性质。只要是所有物品的总重量大于背包容纳量,那么背包一定能装满。这2类问题都具有最优子结构性质,极为相似,但背包问题可以用贪心算法求解,而0-1背包问题却不能用贪心算法求解。
四、实验步骤
装载问题利用贪心算法实现:
#include <iostream>
#include <algorithm>
#include <string.h>
using namespace std;
struct load {
int index;// 集装箱的编号
int wei;// 集装箱的重量
}box[1001];// 集装箱类型的数组,存储集装箱
bool cmp(load a, load b) {
if (a.wei<b.wei) return true;
else return false;
}
int main()
{
int c, n;
int x[1001];// 用于记录集装箱是否被装入,数组的下标即为集装箱的编号
printf("请输入集轮船的载重量c和集装箱的个数n:");
while (scanf("%d %d", &c, &n)!=-1)
{
memset(box, 0, sizeof(box)); // 将数组box的每个值均初始化为0
memset(x, 0, sizeof(x));// 将数组x的每个值均初始化为0
for (int i=1; i<=n; i++)
{
scanf("%d", &box[i].wei);
box[i].index = i;
}
stable_sort(box, box+n+1, cmp);
if (box[1].wei>c) {
printf("No answer!\n");
continue;// 如果第一个集装箱的重量就大于c则后面的均无法装入
}
// 贪心算法的实现,重量最轻者优先装载
int i;
for (i=1; i<=n && box[i].wei<=c; i++)
{
x[box[i].index] = 1;
c -= box[i].wei;// 每装入一个,c同时减少
}
printf("输出装载的集装箱数量:%d\n",i-1);
for (i=1; i<=n; i++)
if (x[i]) printf("输出装载集装箱的编号:%d ", i);
printf("\n");
}
return 0;
}
背包问题利用贪心算法求解(JAVA)
package main;
import java.util.Arrays;
public class B {
static class Three implements Comparable<Three>{
Three(double w,double v){
this.w=w;
this.v=v;
this.p=this.v/this.w;
}
double w;//每个物的重量
double v;//每个物的价值
double p;//性价比
@Override
public int compareTo(Three o){
return (o.p>this.p)?1:-1;
}
}
public static void main(String[] args){
int n=5;//n表示有n个物
double m=150;//m是背包重量
Three[] s=new Three[]{
new Three(50,100),
new Three(40,80),
new Three(60,120),
new Three(10,20),
new Three(50,100)
};
Arrays.sort(s);
double sum=0.0;//sum表示贪心记录物的价值之和
for (int i=0;i<n;i++) {//按照排好的顺序贪心
if(m>s[i].w){//如果物的重量小于背包剩下的重量
m-=s[i].w;
sum+=s[i].v;
}else{
sum+=m*s[i].p;//部分装入
break;
}
}
System.out.println("最大价值:"+sum);
}
}
五、记录与处理(实验数据、误差分析、结果分析)
装载问题:c=200,n=4
C=100,n=5
背包问题:
1.n=5,m=150
2.n=4,m=200
六、思考与总结
主要撰写:
贪心算法理解:
将问题从初始解开始,在每一个阶段都用贪心策略求出当前子集最优策略,逐步靠近目标,尽可能快地求得更好的解。当达到算法中不能再继续前进时,算法终止。贪心算法都是要当前最有利的选择,不考虑未来,而且不能回溯,根据贪心策略来逐步构造问题的解。如果所选的贪心策略不同,则得到的贪心算法就不同。还有就是局部解不一定是最优解,即便不是最优解,也一定是最优解的近似解。贪心法的解题步骤:分解,解决,合并。求解中两个重要性质是最优子结构性质和贪心选择性质。
装载问题理解:
先将集装箱依其重量从大到小排序,然后选择最轻的待选物品,给出选择第一个物品是最轻的,如果第一个集装箱的重量就大于最大承重则后面的均无法装入。接一下也是依次选当前最轻的加入船上,最后合并一起就是最大的装载重量。我还加入一个数组记录集装箱的序号,可以求出是哪个集装箱加入了船上。
装载的不足:我觉得这样的贪心策略,不够灵活,排序后最轻的加入,并不代表能刚刚好装满最大重量。如果剩三个物品,最轻和最重刚好能装满,但是这样的策略就是把最轻和第二重的加入,并没有装满,最后空出来的位置就浪费了。需要优化贪心策略。我也没想清楚。
背包问题:
我们选第三种策略,给每件物品赋予权重=价值/重量,并且按照物品的权重降序排序,依次将物品装入背包。先选取比值最大的物品。比较其重量是否小于背包容量,如果小,就放入,如果大于背包容量,就对其进行切割。循环第一步,直到背包容量等于0。我觉得这样的贪心策略就很完美解决背包问题,不怕装不满,也能收益最大化,在装载问题的不足也能解决。但是贪心算法不能解决0-1背包问题,0-1背包不能分割不能用这样的策略。