第16章 贪心算法
2016-06-13 00:00
381 查看
一、综述
贪心算法是使所做的选择看起来都是当前最佳的,期望通过所做的局部最优选择来产生出一个全局最优解最小生成树算法是贪心算法的一个经典的例子
二、活动选择问题
(1)代码
头文件:https://code.csdn.net/mishifangxiangdefeng/exerciseforalgorithmsecond/tree/master/include/chapter16/chapter16.h产品代码:https://code.csdn.net/mishifangxiangdefeng/exerciseforalgorithmsecond/tree/master/src/chapter16/chapter16.cpp
测试代码:https://code.csdn.net/mishifangxiangdefeng/exerciseforalgorithmsecond/tree/master/tst/chapter16/chapter16Test.cpp
(2)练习
16.1-1
https://code.csdn.net/mishifangxiangdefeng/exerciseforalgorithmsecond/tree/master/src/chapter16/Exercise16_1_1.cpp16.1-2
先对每个活动按照它们的开始时间从小到大排序。令初始时i=n+1,其结束时间无限大,每次选择"结束时间比第i个活动的开始时间早的“的最晚开始活动#include <iostream> #include <algorithm> using namespace std; #define N 11 //一个活动用一个结点表示 struct node { int id; int start; int finish; }A[N+1]; bool cmp(node a, node b) { return a.start < b.start; } //16.1-2 void Greedy_Activity_Selector2() { //先对每个活动按照它们的开始时间从小到大排序 sort(A, A+N+1, cmp); int n = N, i = -1, m; for(m = n; m >= 1; m--) { //找到最后一个“结束时间在第i个活动开始之前”的活动 if(i == -1 || A[m].finish <= A[i].start) { //将这个活动作为执行活动 cout<<A[m].id<<' '; //递归第m个活动执行开始之前的贪心策略 i = m; } } cout<<endl; } /* 0 0 //虚构活动a0 1 4 3 5 0 6 5 7 3 8 5 9 6 10 8 11 8 12 2 13 12 14 */ int main() { int i; //输入测试数据 for(i = 0; i <= N; i++) { A[i].id = i; cin>>A[i].start>>A[i].finish; } Greedy_Activity_Selector2(); return 0; }
16.1-3
见 算法导论-16.1-3常规算法:
针对一个特定的教室,然后做类似于16.1中的工作,从剩余活动中尽量多地安排活动到这个教室。如果活动安排完了,就不需要更多的教室了。如果活动没有安排完,再选择一个新的教室,做同样的工作,直到所有的活动都安排完。
这个算法的时间复杂度是O(n^2),稍换一个角度,就能得到一个O(nlgn)的算
O(lgn)算法:
针对一个特定的活动,为它选择一个适合的教室。对所有已经选择过的教室,用一个最大堆来维护,比较的依据是在这个教室中最早开始的活动的开始时间
具体步骤是这样的:
step1:对所有活动的结束时间从小到大排序
step2:从最后一个活动开始,向第一个活动,依次针对每个活动做以下处理(1)获取堆顶元素的信息(2)如果堆顶的活动开始时间早于当前活动的结束时间,则申请一个新的教室,把活动的开始时间填入其中,再把教室作为一个结点存入堆中(3)如果堆顶的活动开始时间晚于当前活动的结束时间,就把当前活动填入堆顶元素中(4)选择下一个活动,直到所有活动都处理过
三、贪心策略的基本内容
(1)练习
16.2-2
经典的0-1背包问题16.2-3
根据假设w_1 <= w_2 <= ... <= w_n并且v_1 >= v_2 >= ... >= v_n。贪心选择性质:如果该背包问题有解,则一定存在一个最优解包含物品1。
证明:给定一个最优解,如果已经包含物品1,则证明结束。否则用物品1替代背包中任何一个物品i,因为w_1 <
3ff0
;= w_i,所以替换一定是可行的;其次,v_1 >= v_i,所以价值一定不会减小。可见替换后仍然是一个最优解。所以从按物品1,2,...的顺序加入背包,直到不能加入为止。
16.2-4
设ai表示第i个加油站一第i-1个加油站的距离。最后一次加满了油是在第j个站(j初始时为0)
若第k个站满足aj + a|j+1 + …… +ak <= n 且 aj + a|j+1 + …… +a|k+1 > n,那么他应该在第k个站处加油
16.2-5
对所有点进行从小到大的排序,然后每次取值最小的点xi,把区间[xi,xi+1]加入到所求的区间集合中,并把属于区间[xi,xi+1]的点xj从点集中除去。16.2-6
见算法导论-16.2-6常规算法:
先求avgi = vi/wi,按照avgi从大到小排序,再贪心选择,时间复杂度为O(nlgn)
改进:
更一般的情况,不需要排序,例如:如果a1, a2,a3是avgi 最大的三个物品,如果它们的总量和不大于W,我们是不需要知道它们间的相对顺序的。
O(n)算法:
选择一个物品作为主元,对所有物品进行Paitition操作。将avg 分为三个集合G = {ai: avgi< m}, Q = {ai:avgi= m}, P = {ai: avgi> m}。分别对G, Q, P中元素求和得SG, SQ, SP。
1. 如果W < SG, 则令物体的集合为O = G,对集合O递归进行上述过程。
2. 如果 SG<=W <= SG+SQ,则将集合G中的元素都放入包中,并将集合Q总元素尽可能多的放入包中,结束。
3. 如果 SG+SQ < W, 将G,Q中元素放入包中。令物体集合O = P,总重W = W - SG - SQ。递归进行上述过程。
16.2-7
对集合A和B进行排序,每次取最大值,计算a^b
四、哈夫曼编码
(1)代码
#include <iostream> #include "Heap.h" using namespace std; #define N 6 extern int length; int result[N+1]; //哈夫曼树 struct Huffman_Tree { node *root; Huffman_Tree():root(NULL){} }; //哈夫曼编码 void Huffman(unsigned int *A, Huffman_Tree *T) { int n = N; while(n > 1) { //生成一个新的结点 node *z = new node; //选择堆顶元素作为其左结点 z->left = (node *)Heap_Extract_Min(A); //选择堆顶元素作为其右结点 z->right = (node *)Heap_Extract_Min(A); z->p = z->left->p + z->right->p; //将新结点插入堆中 Min_Heap_Insert(A, (unsigned int)z); n--; } //剩下最后一个结点时,这个结点就是树的根结点 T->root = (node *)Heap_Extract_Min(A); } //输出这棵树上每个字母的编码 void PrintTree(node *t, int cnt) { int i; //叶子结点 if(t->data != -1) { //输出其值及编码 cout<<t->data<<' '; for(i = 0; i < cnt; i++) cout<<result[i]; cout<<endl; return ; } //非叶子结点,对其孩子进行递归 result[cnt] = 0; PrintTree(t->left, cnt+1); result[cnt] = 1; PrintTree(t->right, cnt+1); } /* f 5 e 9 c 12 b 13 d 16 a 45 */ int main() { unsigned int A[N+1] = {};//存储的是结点的地址 length = N; int i; char c; double p; //构造N个结点 for(i = 1; i <= N; i++) { cin>>c>>p; node *z = new node(c, p); A[i] = (unsigned int)z; } //构造最小堆 Build_Min_Heap(A); //构造哈夫曼树 Huffman_Tree *T = new Huffman_Tree(); Huffman(A, T); //输出编码结果 PrintTree(T->root, 0); return 0; }
(2)习题
16.3-2
能16.3-6
#include <iostream> #include "Heap.h"//选择p最小的结点时要借助于堆来实现 using namespace std; #define N 6 extern int length; int result[N+1]; //哈夫曼树 struct Huffman_Tree { node *root; Huffman_Tree():root(NULL){} }; //哈夫曼编码 void Huffman(unsigned int *A, Huffman_Tree *T) { int n = N; while(n > 1) { //生成一个新的结点 node *z = new node; if(n--) { //选择堆顶元素作为其左结点 z->left = (node *)Heap_Extract_Min(A); z->p = z->p + z->left->p; } if(n--) { //选择堆顶元素作为其中间结点 z->middle = (node *)Heap_Extract_Min(A); z->p = z->p + z->middle->p; } if(n--) { //选择堆顶元素作为其右结点 z->right = (node *)Heap_Extract_Min(A); z->p = z->p + z->right->p; } //将新结点插入堆中 Min_Heap_Insert(A, (unsigned int)z); n++; } //剩下最后一个结点时,这个结点就是树的根结点 T->root = (node *)Heap_Extract_Min(A); } //输出这棵树上每个字母的编码 void PrintTree(node *t, int cnt) { int i; //叶子结点 if(t->data != -1) { //输出其值及编码 cout<<t->data<<' '; for(i = 0; i < cnt; i++) cout<<result[i]; cout<<endl; return ; } //非叶子结点,对其孩子进行递归 result[cnt] = 0; PrintTree(t->left, cnt+1); result[cnt] = 1; PrintTree(t->middle, cnt+1); if(t->right) { result[cnt] = 2; PrintTree(t->right, cnt+1); } } /* f 5 e 9 c 12 b 13 d 16 a 45 */ int main() { unsigned int A[N+1] = {};//存储的是结点的地址 length = N; int i; char c; double p; //构造N个结点 for(i = 1; i <= N; i++) { cin>>c>>p; node *z = new node(c, p); A[i] = (unsigned int)z; } //构造最小堆 Build_Min_Heap(A); //构造哈夫曼树 Huffman_Tree *T = new Huffman_Tree(); Huffman(A, T); //输出编码结果 PrintTree(T->root, 0); return 0; }
五、思考题
16-1 找换硬币
a)每次都尽量选最大的、c)1 4 5
d)找换i分钱的最少硬币数是ans[i],ans[i] = max{ans[i-s[j]]+1}
#include <iostream> #include <algorithm> using namespace std; //用于排序 bool cmp(int a, int b) { return a < b; } int main() { int n, k, i, j; //输入数据 cin>>n>>k; int s[100], ans[100]; for(i = 0; i < k; i++) cin>>s[i]; //对硬币从小到大排序 sort(s, s + k, cmp); //找换i分钱的最少硬币数是ans[i] memset(ans, 0, sizeof(ans)); //ans[i] = max{ans[i-s[j]]+1} for(i = 1; i <= n; i++) { for(j = 0; s[j] <= i; j++) { if(ans[i] == 0 || ans[i-s[j]]+1 < ans[i]) ans[i] = ans[i-s[j]]+1; } } cout<<ans <<endl; return 0; }
16-2 最小化平均结束时间的调度
a)每当一个任务执行完时,就选则剩下的任务中选择执行时间最短的任务执行b)每当一个任务执行完时,就选则剩下的任务中选择剩余执行时间最短的任务执行,每当一个新的任务到来时,如果它的执行时间比当前任务的剩余执行时间要短,就抢占
相关文章推荐
- 算法导论 第22章 图算法 22.3 深度优先搜索
- 算法导论-14.3-6-MIN-GAP
- 算法导论 14.1-7 顺序统计树求逆序对 O(nlgn)
- 算法导论-15-4-计划一个公司聚会
- 算法导论-13-1-持久动态集合
- Linux2.6为数据结构分配内存-slab
- 算法导论-15-7-达到最高效益的调度
- 算法导论 第10章 10.4 有根树的表示
- 算法导论 第13章 红黑树
- 算法导论 第7章 快速排序
- 算法导论 第12章 二叉查找树
- 算法导论-24.2-有向无回路图中的单源最短路径
- 算法导论 第10章 10.1 栈和队列
- 算法导论 11.1-4 大数组的直接寻址表
- 算法导论-14-1-最大重叠点
- 算法导论-12-2-基数树
- 算法导论-14-2-Josephus排列
- 算法导论-8-3-排序不同长度的数据项
- 算法导论 10.4-2 O(n)时间 递归遍历二叉树
- 算法导论-9.3-3-快速排序-最坏时间O(nlgn)