贪心算法之活动选择问题
来源:互联网 发布:国泰君安证券mac版 编辑:程序博客网 时间:2024/05/09 21:18
贪心算法之活动选择问题
我们在之前的文章里面已经提到过动态规划的方法来求解最优的问题,但是就是因为动态规划太过于强大,像一把瑞士军刀,在一些比较特殊的问题上再使用动态规划的话,就有点用脸盆刷牙的感觉了,而且动态规划运行时间也比较长。对于一些特殊的最优解问题不是很适合,于是我们就有了贪心算法的出现。
贪心算法的座右铭:每一步都尽量做到最优,最终结果就算不是最优,那么也是次最优
从上面的描述中知道,贪心算法不是每一个最优解问题都可以得到最优解的,但是最终的解也是趋近于最优解的!
下面我们就借用活动选择问题来理解一下贪心算法
问题描述
假设我们存在这样一个活动集合
比如下面的活动集合
我们假定在这个活动集合里面,都是按照
即:
从上面可见,我们观察可得兼容子集有:
活动选择问题的最优子结构
在开始分析之前,我们首先定义几种写法
-
-
我们假设在有活动集合
因此我们有
这里我们发现与之前讲过的动态规划有点类似,我们可以得到动态规划的递归式子:
如果我们不知道
这里我们首先分析一下动态规划的代价,我们这里子问题数量为
贪心算法
假设我们无需考察所有的子问题就可将一个集合加入到最优解里面,将会怎样?,这将会使我们省去所有的递归考察过程。实际上,对于活动选择问题,我们只需考察一种选择:贪心选择
对于活动选择问题来说,什么是贪心选择呢?那就是选取一个活动,使得去掉这个活动以后,剩下来的资源最多。那么这里怎么选择才能使得剩下来的资源最多呢?我们这里共享的资源是什么?就是大家共有的哪一个时间段呀,我们首先想到肯定是占用时间最短的呀,即
因为我们给出的集合
证明:
令
递归贪心算法
上面我们已经知道了贪心选择是什么,现在我们来看看怎么实现,我们首先选出最早结束的活动
选择最早结束活动的贪心选择代码实现
递归方式
/************************************************** @Filename: activitySelector_v5.cc* @Author: qeesung* @Email: qeesung@qq.com* @DateTime: 2015-04-23 14:21:27* @Version: 1.0* @Description: 这里采用贪心算法的递归方式来解决最大兼容自问题**************************************************/#include <iostream>#include <utility>#include <vector>#include <string>#include <cstdio>#include <cstdlib>#include <cstring>using namespace std;#define BufSize 20// 用来存储解决方案char buf[BufSize];std::vector<string> solution;size_t dealGreatActivitySelector(std::vector<pair<int , int> > & activities , int left , int right);size_t greateActivitySelector(std::vector<pair<int , int> > & activities){ if(activities.size() == 0) return 0; return dealGreatActivitySelector(activities , 1 , activities.size()-1);}size_t dealGreatActivitySelector(std::vector<pair<int , int> > & activities , int left , int right){ if(left > right) return 0; // 找到第一个边界,使得与activies[left]兼容 int newLeft = left; while(newLeft <= right && activities[left].second > activities[newLeft].first) newLeft++; snprintf(buf , BufSize , "a%d" , left); solution.push_back(string(buf , buf+BufSize)); memset(buf , BufSize , 0); return dealGreatActivitySelector(activities , newLeft , right)+1;}void printSolution(){ for (std::vector<string>::iterator i = solution.begin(); i != solution.end(); ++i) { cout<<*i<<"\t"; } cout<<endl;}int main(int argc, char const *argv[]){ std::vector<pair<int , int> > activities; activities.push_back(pair<int , int>(0,0)); activities.push_back(pair<int , int>(1,4)); activities.push_back(pair<int , int>(3,5)); activities.push_back(pair<int , int>(0,6)); activities.push_back(pair<int , int>(5,7)); activities.push_back(pair<int , int>(3,9)); activities.push_back(pair<int , int>(5,9)); activities.push_back(pair<int , int>(6,10)); activities.push_back(pair<int , int>(8,11)); activities.push_back(pair<int , int>(8,12)); activities.push_back(pair<int , int>(2,14)); activities.push_back(pair<int , int>(12,16)); cout<<"The max selectors is : "<<greateActivitySelector(activities)<<endl; printSolution(); return 0;}
迭代的方式进行
/************************************************** @Filename: activitySelector_v6.cc* @Author: qeesung* @Email: qeesung@qq.com* @DateTime: 2015-04-23 14:35:43* @Version: 1.0* @Description: 迭代的贪心算法解决最大兼容子集**************************************************/#include <iostream>#include <vector>#include <utility>#include <cstring>#include <cstdlib>#include <cstdio>using namespace std;#define BufSize 20// 用来存储解决方案char buf[BufSize];std::vector<string> solution;size_t dealGreatActivitySelector(std::vector<pair<int , int> > & activities , int left , int right);size_t greateActivitySelector(std::vector<pair<int , int> > & activities){ if(activities.size() == 0) return 0; return dealGreatActivitySelector(activities , 1 , activities.size()-1);}size_t dealGreatActivitySelector(std::vector<pair<int , int> > & activities , int left , int right){ if(left > right) return 0; int count = 1; snprintf(buf , BufSize , "a%d" , left); solution.push_back(string(buf , buf+BufSize)); memset(buf , BufSize , 0); int lastPos=left; for (int i = left+1; i <= right; ++i) { // 不断的寻找边界 while(i<= right && activities[i].first < activities[lastPos].second) ++i; if(i > right) break; //找到就加入到solution里面 snprintf(buf , BufSize , "a%d" , i); solution.push_back(string(buf , buf+BufSize)); memset(buf , BufSize , 0); lastPos = i; count++; } return count;}void printSolution(){ for (std::vector<string>::iterator i = solution.begin(); i != solution.end(); ++i) { cout<<*i<<"\t"; } cout<<endl;}int main(int argc, char const *argv[]){ std::vector<pair<int , int> > activities; activities.push_back(pair<int , int>(0,0)); activities.push_back(pair<int , int>(1,4)); activities.push_back(pair<int , int>(3,5)); activities.push_back(pair<int , int>(0,6)); activities.push_back(pair<int , int>(5,7)); activities.push_back(pair<int , int>(3,9)); activities.push_back(pair<int , int>(5,9)); activities.push_back(pair<int , int>(6,10)); activities.push_back(pair<int , int>(8,11)); activities.push_back(pair<int , int>(8,12)); activities.push_back(pair<int , int>(2,14)); activities.push_back(pair<int , int>(12,16)); cout<<"The max selectors is : "<<greateActivitySelector(activities)<<endl; printSolution(); return 0;}
选择最短时长的活动贪心算法实现
递归方式进行
/************************************************** @Filename: activitySelector_v7.cc* @Author: qeesung* @Email: qeesung@qq.com* @DateTime: 2015-04-23 14:21:27* @Version: 1.0* @Description: 这里采用贪心算法的递归方式来解决最大兼容自问题**************************************************/#include <iostream>#include <utility>#include <vector>#include <string>#include <cstdio>#include <cstdlib>#include <cstring>using namespace std;#define BufSize 20// 用来存储解决方案char buf[BufSize];std::vector<string> solution;size_t dealGreatActivitySelector(std::vector<pair<int , int> > & activities , int left , int right);size_t greateActivitySelector(std::vector<pair<int , int> > & activities){ if(activities.size() == 0) return 0; return dealGreatActivitySelector(activities , 1 , activities.size()-1);}size_t dealGreatActivitySelector(std::vector<pair<int , int> > & activities , int left , int right){ if(left > right) return 0; //首先找到消耗最小的那个 int minPos = left; int min = 100; for(int i = left ; i < right ; ++i) { if((activities[i].second-activities[i].first) < min) { min = activities[i].second-activities[i].first; minPos = i; } } snprintf(buf , BufSize , "a%d" , left); solution.push_back(string(buf , buf+BufSize)); memset(buf , BufSize , 0); int leftTemp = minPos; int rightTemp = minPos; /** 找到左边界 */ while(leftTemp >= left && activities[leftTemp].second > activities[minPos].first ) leftTemp--; /** 找到右边界 */ while(rightTemp <= right && activities[rightTemp].first < activities[minPos].second) rightTemp++; return dealGreatActivitySelector(activities , left , leftTemp)+\ dealGreatActivitySelector(activities , rightTemp, right)+1; }void printSolution(){ for (std::vector<string>::iterator i = solution.begin(); i != solution.end(); ++i) { cout<<*i<<"\t"; } cout<<endl;}int main(int argc, char const *argv[]){ std::vector<pair<int , int> > activities; activities.push_back(pair<int , int>(0,0)); activities.push_back(pair<int , int>(1,4)); activities.push_back(pair<int , int>(3,5)); activities.push_back(pair<int , int>(0,6)); activities.push_back(pair<int , int>(5,7)); activities.push_back(pair<int , int>(3,9)); activities.push_back(pair<int , int>(5,9)); activities.push_back(pair<int , int>(6,10)); activities.push_back(pair<int , int>(8,11)); activities.push_back(pair<int , int>(8,12)); activities.push_back(pair<int , int>(2,14)); activities.push_back(pair<int , int>(12,16)); cout<<"The max selectors is : "<<greateActivitySelector(activities)<<endl; printSolution(); return 0;}
采用动态规划方式实现
为了与动态规划有对比,我们这里采用两种动态规划的方式实现,一种自上而下,一种自下而上:
自上而下的实现
/************************************************** @Filename: activitySelector_v2.cc* @Author: qeesung* @Email: qeesung@qq.com* @DateTime: 2015-04-23 12:58:05* @Version: 1.0* @Description: 带有解决方案的最大兼容子集-动态规划自上而下的算法**************************************************/#include <iostream>#include <utility>#include <vector>using namespace std;/** 最大活动的数目 */#define MAX_ACTIVITY_NUM 20size_t dealGreatActivitySelector(std::vector<pair<int , int> > & activities , int left , int right);size_t great[MAX_ACTIVITY_NUM][MAX_ACTIVITY_NUM];//用来存储i到j的最大子集数目size_t solution[MAX_ACTIVITY_NUM][MAX_ACTIVITY_NUM];//用来存储选择pair<int , int> border[MAX_ACTIVITY_NUM][MAX_ACTIVITY_NUM];//用来存储边界值/** * 最大的兼容子集 * @param activities 活动的链表,已经按照结束时间的先后顺序拍好了 * @return 返回最大兼容的数量 */size_t greateActivitySelector(std::vector<pair<int , int> > & activities){ if(activities.size() == 0) return 0; dealGreatActivitySelector(activities , 0 , activities.size()-1); return great[0][activities.size()-1];}/** * 实际处理最大兼容子集的函数 * @param activities 活动 * @param left 左边界 * @param right 右边界 * @return left到right的最大兼容子集数 */size_t dealGreatActivitySelector(std::vector<pair<int , int> > & activities , int left , int right){ if(left > right) return 0; // 只有一个活动 if(left == right) { great[left][right] = 1; solution[left][right] = left; return 1; } if(great[left][right] != 0) return great[left][right];// 之前已经算过 //求解过程 int max = 0; int pos = left; pair<int , int> borderTemp; for (int i = left; i <= right ; ++i) { //////////////////////////// //以i为基准,向两边找到不与i活动相交的集合 // //////////////////////////// int leftTemp = i; int rightTemp = i; /** 找到左边界 */ while(leftTemp >= left && activities[leftTemp].second > activities[i].first ) leftTemp--; /** 找到右边界 */ while(rightTemp <= right && activities[rightTemp].first < activities[i].second) rightTemp++; int temp = dealGreatActivitySelector(activities , left , leftTemp)+\ dealGreatActivitySelector(activities , rightTemp , right)+1; if(temp > max) { max = temp; pos = i ; borderTemp = pair<int , int>(leftTemp , rightTemp); } } solution[left][right] = pos; border[left][right] = borderTemp; great[left][right] = max; return max;}void printSolution(int left , int right){ if(left > right) return; if(left == right) { cout<<"from "<<left<<" to "<<right<<" -----> "<<solution[left][right]<<endl; return; } cout<<"from "<<left<<" to "<<right<<" -----> "<<solution[left][right]<<endl; printSolution(left , border[left][right].first); printSolution(border[left][right].second , right); return;}int main(int argc, char const *argv[]){ std::vector<pair<int , int> > activities; activities.push_back(pair<int , int>(1,4)); activities.push_back(pair<int , int>(3,5)); activities.push_back(pair<int , int>(0,6)); activities.push_back(pair<int , int>(5,7)); activities.push_back(pair<int , int>(3,9)); activities.push_back(pair<int , int>(5,9)); activities.push_back(pair<int , int>(6,10)); activities.push_back(pair<int , int>(8,11)); activities.push_back(pair<int , int>(8,12)); activities.push_back(pair<int , int>(2,14)); activities.push_back(pair<int , int>(12,16)); cout<<"The max selectors is : "<<greateActivitySelector(activities)<<endl; printSolution(0 , activities.size()-1); return 0;}
自下而上的实现
/************************************************** @Filename: activitySelector_v4.cc* @Author: qeesung* @Email: qeesung@qq.com* @DateTime: 2015-04-23 13:52:00* @Version: 1.0* @Description: 最大兼容子集 带有解决方案的自下而上的动态规划算法**************************************************/#include <iostream>#include <utility>#include <vector>#include <string>#include <stdlib.h>#include <stdio.h>using namespace std;/** 最大活动的数目 */#define MAX_ACTIVITY_NUM 20void dealGreatActivitySelector(std::vector<pair<int , int> > & activities , int left , int right);size_t great[MAX_ACTIVITY_NUM+1][MAX_ACTIVITY_NUM+1];//用来存储i到j的最大子集数目size_t solution[MAX_ACTIVITY_NUM+1][MAX_ACTIVITY_NUM+1];//用来存储选择pair<int , int> border[MAX_ACTIVITY_NUM+1][MAX_ACTIVITY_NUM+1];//用来存储边界值/** * 最大的兼容子集 * @param activities 活动的链表,已经按照结束时间的先后顺序拍好了 * @return 返回最大兼容的数量 */size_t greateActivitySelector(std::vector<pair<int , int> > & activities){ if(activities.size() == 0) return 0; dealGreatActivitySelector(activities , 1 , activities.size()-1); return great[1][activities.size()-1];}/** * 实际处理最大兼容子集的函数 * @param activities 活动 * @param left 左边界 * @param right 右边界 * @return left到right的最大兼容子集数 */void dealGreatActivitySelector(std::vector<pair<int , int> > & activities , int left , int right){ // 只有一个活动,初始化 for (int i = left; i < right; ++i) { great[i][i-1] = 0; } for(int k = 0 ; k <= right-left ; ++k) { for (int i = left; i <=right ; ++i) { int max = 0; int pos = i; int leftBorder=i; int rightBorder=i; for(int j = i ; j <= i+k ; ++j) { // 首先需要计算左右边界 int leftTemp = j; int rightTemp = j; /** 找到左边界 */ while(leftTemp >= i && activities[leftTemp].second > activities[j].first ) leftTemp--; /** 找到右边界 */ while(rightTemp <= i+k && activities[rightTemp].first < activities[j].second) rightTemp++; int temp = great[i][leftTemp]+great[rightTemp][i+k]+1; if(max < temp) { max = temp; pos = j; leftBorder = leftTemp; rightBorder = rightTemp; } } solution[i][i+k] = pos; border[i][i+k] = pair<int , int>(leftBorder , rightBorder); great[i][i+k] = max; } }}void printSolution(int left , int right){ if(left > right) return; if(left == right) { cout<<"from "<<left<<" to "<<right<<" -----> "<<solution[left][right]<<endl; return; } cout<<"from "<<left<<" to "<<right<<" -----> "<<solution[left][right]<<endl; printSolution(left , border[left][right].first); printSolution(border[left][right].second , right); return;}int main(int argc, char const *argv[]){ std::vector<pair<int , int> > activities; activities.push_back(pair<int , int>(0,0)); activities.push_back(pair<int , int>(1,4)); activities.push_back(pair<int , int>(3,5)); activities.push_back(pair<int , int>(0,6)); activities.push_back(pair<int , int>(5,7)); activities.push_back(pair<int , int>(3,9)); activities.push_back(pair<int , int>(5,9)); activities.push_back(pair<int , int>(6,10)); activities.push_back(pair<int , int>(8,11)); activities.push_back(pair<int , int>(8,12)); activities.push_back(pair<int , int>(2,14)); activities.push_back(pair<int , int>(12,16)); cout<<"The max selectors is : "<<greateActivitySelector(activities)<<endl; printSolution(1,activities.size()-1); return 0;}
结论
通过上面的分析我们可知贪心算法也是要有最优子结构的,而且一旦决定了一种贪心选择,那么速度是远远快于动态规划的,难就难在怎么决定贪心选择,到底怎么选是贪心算法的难点
- 贪心算法之活动选择问题
- 贪心算法之活动选择问题
- 贪心算法之活动选择问题
- 贪心算法之活动选择问题
- 贪心算法之活动选择问题
- 贪心算法之活动选择问题
- 贪心算法活动选择问题
- 贪心算法---活动选择问题
- 贪心算法-活动选择问题
- 贪心算法--活动选择问题
- 贪心算法-活动选择问题
- 贪心算法之活动选择
- 《算法导论》之 贪心算法—活动选择问题
- 【算法导论】贪心算法之活动选择问题
- 算法导论之贪心算法:活动选择问题
- 【算法导论】贪心算法之活动选择问题
- 贪心之活动选择问题
- 贪心之活动选择问题
- Ubuntu下的mysql安装
- 程序设计中几种常见的命名规则
- 机房合作登录——装饰模式
- jquery.ajaxSubmit
- Linux中crond服务与crontab用法
- 贪心算法之活动选择问题
- window版分布式缓存Memcached使用攻略
- DBMS_STATS.GATHER_TABLE_STATS详解
- 黑马程序员——java基础(字符串、基本类型包装类)
- 在HTML5中不再支持的script的属性
- mini2440硬件篇之开篇词
- HDU ACM 1827 Summer Holiday->强连通分量+缩点(tarjan算法)
- Mysql事件学习
- 2015.4.25.14.03_Singleton_2015.4.25_ 单例模式的七种写法&懒汉式&饿汉式