针对经典的背包问题(0-1背包问题利用动态规划算法可以很好的解决)
下面是一个可以试用贪心算法解的题目,贪心解的确不错,可惜不是最优解。
[背包问题]有一个背包,背包容量是M=150。有7个物品,物品可以分割成任意大小。
要求尽可能让装入背包中的物品总价值最大,但不能超过总容量。
物品 A B C D E F G
重量 35 30 60 50 40 10 25
价值 10 40 30 50 35 40 30
分析:
目标函数: ∑pi最大
约束条件是装入的物品总重量不超过背包容量:∑wi<=M( M=150)
(1)根据贪心的策略,每次挑选价值最大的物品装入背包,得到的结果是否最优?
(2)每次挑选所占重量最小的物品装入是否能得到最优解?
(3)每次选取单位重量价值最大的物品,成为解本题的策略。
值得注意的是,贪心算法并不是完全不可以使用,贪心策略一旦经过证明成立后,它就是一种高效的算法。
贪心算法还是很常见的算法之一,这是由于它简单易行,构造贪心策略不是很困难。
可惜的是,它需要证明后才能真正运用到题目的算法中——应用时需要先证明。
一般来说,贪心算法的证明围绕着:整个问题的最优解一定由在贪心策略中存在的子问题的最优解得来的。
对于例题中的3种贪心策略,都是无法成立(无法被证明)的,解释如下:
(1)贪心策略:选取价值最大者。反例:
M=30
物品:A B C
重量:28 12 12
价值:30 20 20
根据策略,首先选取物品A,接下来就无法再选取了,可是,选取B、C则更好。
(2)贪心策略:选取重量最小。它的反例与第一种策略的反例差不多。
(3)贪心策略:选取单位重量价值最大的物品。反例:
M=30
物品:A B C
重量:28 20 10
价值:28 20 10
根据策略,三种物品单位重量价值一样,程序无法依据现有策略作出判断,如果选择A,则答案错误。
三种贪心策略均无法成立,但是针对特定的部分背包问题,贪心算法确实是可能成立的(针对上面的例子),但是也只能说可以用,并不能确定是最优解,只能说贪心算法不普适于部分背包问题。
C++ code
#include <iostream> #include <map> #include <algorithm> #include <vector> #include <iomanip> using namespace std; //按value找到map中对应的key,并返回 int finder(float value_per_weight,map<int,float> value_sort) { map<int,float>::iterator iter = value_sort.begin(); for (iter;iter != value_sort.end(); iter++) { if (iter->second == value_per_weight) { return iter->first; } } } int main(int argc, char**argv) { //设定保留小数的位数 cout<< setprecision(5)<<fixed<<endl; //问题条件设置 float package = 150; int value[7] = {10,40,30,50,35,40,30}; int weight[7] = {35,30,60,50,40,10,25}; char cargo[7] = {'A','B','C','D','E','F','G'}; char new_cargo[7] = {'A','B','C','D','E','F','G'}; //存放原始的序号、均价值对应关系的map map<int,float> value_sort; for (int i = 0; i<7 ; i++) { value_sort[i]; } //均价值 vector<float> value_per_weight(7); for (int i = 0; i<7 ; i++) { value_per_weight[i] = float(value[i]) / float(weight[i]); } //均价值赋值map map<int,float>::iterator iter = value_sort.begin(); int j = 0; for (iter;iter != value_sort.end(); iter++) { iter->second = value_per_weight[j]; ++j; } //排序均价值 sort(value_per_weight.begin(),value_per_weight.end()); //贪心计算每件物品的放入 float count[7] ={0}; for (int i = 6; i >= 0 ; i--) { if (package > 0) { int index = finder(value_per_weight[i],value_sort); count[i] =float(weight[index]); if (package < weight[index]) { count[i] = package; } package = package - count[i]; } } for (int i = 6; i >= 0 ; i--) { int index = finder(value_per_weight[i],value_sort); new_cargo[i] = cargo[index]; } //输出计算结果 for (int i = 0; i<7 ; i++) { cout<<new_cargo[i]<<" "; } cout<<endl; for (int i = 0; i<7 ; i++) { cout<<count[i]<<" "; } system("pause"); return 0; } 设置完毕后,后续的cout也会改变,显示float的小数,头文件iomanip // float j = 2/2 ; // cout<< showpoint<<j<<endl; // cout<< setprecision(5)<<fixed<<j<<endl
小船过河问题
POJ1700是一道经典的贪心算法例题。题目大意是只有一艘船,能乘2人,船的运行速度为2人中较慢一人的速度,过去后还需一个人把船划回来,问把n个人运到对岸,最少需要多久。先将所有人过河所需的时间按照升序排序,我们考虑把单独过河所需要时间最多的两个旅行者送到对岸去,有两种方式:
1.最快的和次快的过河,然后最快的将船划回来;次慢的和最慢的过河,然后次快的将船划回来,所需时间为:t[0]+2*t[1]+t[n-1];
2.最快的和最慢的过河,然后最快的将船划回来,最快的和次慢的过河,然后最快的将船划回来,所需时间为:2*t[0]+t[n-2]+t[n-1]。
算一下就知道,除此之外的其它情况用的时间一定更多。每次都运送耗时最长的两人而不影响其它人,问题具有贪心子结构的性质。
C++ code
#include <iostream> #include <algorithm> using namespace std; int main(int argc, char**argv) { int total_time = 0; cout<<" How many people ?"<<endl; int number_of_people ; cin>>number_of_people; if (number_of_people > 0) { int time_of_people[1000]; cout<<"Please input the time needed to go through the river of each person and by up grade"<<endl; for (int i = 0; i < number_of_people; i++) { cin >> time_of_people[i]; } while (number_of_people > 3) { total_time = total_time+min(2*time_of_people[0]+ time_of_people[number_of_people-1]+ time_of_people[number_of_people-2],time_of_people[0]+ 2*time_of_people[1]+ time_of_people[number_of_people-1]); number_of_people -= 2; } if (number_of_people == 3) { total_time = total_time + time_of_people[0]+ time_of_people[1]+ time_of_people[2]; } else if (number_of_people == 2) { total_time = total_time + time_of_people[1]; } else { total_time = total_time + time_of_people[0]; } cout<<"The total time needed is :"<<total_time<<endl; } else { cout<<"No people or wrong number"<<endl; } system("pause"); return 0; }