《趣学算法》之贪心算法(上)

一个贪心算法总是做出当前最好的选择,也就是说,它期望通过局部最优选择从而得到全局最优的解决方案。 ——《算法导论》

贪心算法秘籍

  • 贪心策略(即选择当前状态下最优解决方案)
  • 局部最优解
  • 全局最优解
    【使用贪心算法的例子:冒泡排序——贪心策略是每一次从剩下的序列中挑一个最大的数,把这些选出来的数放在一起,就得到了从大到小的排序结果。】
    冒泡排序
    贪心算法应用
    ①加勒比海盗——最优装载问题

有一天,加勒比海盗们截获了一艘装满古董的货船,每件古董都价值连城,一旦打碎就失去价值。虽然海盗船足够大,但载重量为 C,每件古董的重量为 Wi ,海盗们该如何把尽可能多数量的古董装上海盗船?

思考过程】要求装的古董数量尽可能多,而船的载重量固定,则优先装重量小的物品。

算法设计】(1)当载重量固定为 C 时,Wi 越小,可装载的古董数量 n 越大。 (2)把 n 个古董的重量从小到大排序,根据贪心策略依次选出 i 个古董,直到海盗船装不下。
代码实现

#include <iostream>
#include <algorithm>
const int N = 1000005;
using namespase std;
double w[N];  //古董的重量数组
int main(){
	double c;
	int n;
	cout<< "请输入载重量 c 及古董个数 n : "<<endl;
	cin>>c>>n;
	cout<<"请输入每个古董的重量,用空格分开: "<<endl;
	for(int i=0;i<n;i++){
		tmp+=w[i];
		if(tmp<=c)
			ans ++;
		else
			break;
	}
	cout<<"能装入的古董最大数量为 Ans= ";
	cout<<ans<<endl;
	return 0;
}

②阿里巴巴与四十大盗——背包问题

假设山洞里有 n 种宝物,每种宝物有一定重量 w 和相应的价值 v ,毛驴运载能力有限,只能运走 m 重量的宝物,一种宝物只能拿一样,宝物可分割。那么如何使毛驴运走最大价值的宝物?

思考过程】如果选价值量最大的宝物,那重量不一定小,不行;如果选重量最小的宝物,那价值不一定高,也不行;每次选取单位重量价值最大的宝物,即每次选择性价比(价值/重量)最高的宝物,达到运载重量 m ,那一定是价值最大的。

算法设计】(1)数据结构及初始化。将 n 种宝物的重量和价值存储在结构体 three(含重量、价值、性价比),按性价比从高到底排序。用 sum 来存储毛驴能够运走的最大价值,初始量为 0。 (2)根据贪心策略,按性价比从大到小选宝物,直到达到毛驴的运载能力。每次选性价比高的物品,判断是否小于 m ,若小于则放入,sum加上当前宝物的价值,m减去当前宝物的重量;若大于,则取该宝物的一部分 m*p[i] , m=0 ,程序结束。m减少到0,则sum得到最大值。
代码实现

#include <iostream>
#include <algorithm>
const int N = 1000005;
using namespase std;
struct three{
	double w; //每个宝物的重量
	double v; //每个宝物的价值
	double p; //性价比
}s[M];
bool cmp(three a,three b){
	return a.p > b.p; //根据宝物的单位价值从大到小排序
}
int main(){
	int n; //表示有n个宝物
	double m; //m表示毛驴的承载能力
	cout<< "请输入宝物数量 n 及毛驴的承载能力 m : "<<endl;
	cin>>n>>m;
	cout<<"请输入每个每个宝物的重量和价值,用空格分开: "<<endl;
	for(int i=0;i<n;i++){
		cin>>a[i].w>>s[i].v;
		s[i].p=s[i].v/s[i].w; //每个宝物单位价值
	}
	sort(s,s+n,cmp);
	double sum=0.0; //sum表示贪心记录运走宝物的价值之和
	for(int i=0;i<n;i++){  //按照排好的顺序贪心
		if(m>s[i].w){  //如果宝物的重量小于毛驴剩下的承载能力
			m-=s[i].w;
			sm+=s[i].v;
		}
		else{  //如果宝物的重量大于毛驴剩下的承载能力
			sum+=m*s[i].p;  //部分装入
			break;
		}
	}
	cout<<"装入宝物的最大价值Maximum value="<<sum<<endl;
	return 0;
}

问题拓展】0-1背包:物品不可分割的装载问题。

③高级钟点秘书——会议安排

“钟点秘书”,是指年轻白领女性利用工余时间为客户提供秘书服务,并按钟点收取酬金。“钟点秘书”为客户提供有偿服务的方式一般是:采用电话、电传、上网等“遥控”式服务,或亲自到客户公司处理部分业务。其服务对象主要有三类:一是外地前来考察商务经营、项目投资的商人或政要人员;二是前来开展短暂商务活动,或召开小型资讯发布会的国外客商;三是本地一些请不起长期秘书的企、事业单位。
某跨国公司总裁正分身无术,为一堆会议时间表焦头烂额,希望高级钟点秘书能做出合理的安排,能在有限的时间内召开更多的会议。

思考过程】在会议安排中,每个会议 i 都有起始时间 bi 和结束时间 ei ,且 bi < ei ,即一个会议进行的时间为半开区间 [bi , ei)。如果 [bi , ei)和[bj , ej) 均在“有限时间内”,且不相交,则称会议 i 和会议 j 相容的。要让会议数最多,我们需要选择最多的不相交时间段。我们可以尝试贪心策略:
(1)每次选择开始时间最早且与已安排的会议相容的会议;(不行)
(2)每次选择持续时间最短且与已安排的会议相容的会议;(不行)
(3)每次选择结束时间最早且与已安排的会议相容的会议。(行)

算法设计】(1)初始化,将n个会议的开始时间、结束时间存放在结构体数组中,然后按结束时间从小到大排序,结束时间相等的,按开始时间从大到小排序。 (2)选择第一个具有最早结束时间的会议, 用 last 记录刚选中会议的结束时间。 (3)依次从剩下未安排的会议中选择,若会议 i 开始时间大于等于最后一个选中的会议的结束时间 last ,那么会议 i 与已选中的会议相容,可以安排,更新 last 为刚选中会议的结束时间;否则,舍弃会议 i ,检查下一个会议是否可以安排。
代码实现

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
struct Meet{
	int beg; //会议开始时间
	int end; //会议结束时间
	int num; //记录会议编号
}meet[1000]; //会议的最大个数为1000
class setMeet{
	public:
		void init();
		void solve();
	private:
		int n,ans; //n:会议总数  ans:最大的安排会议总数
};
//读入数据
void setMeet::init(){
	int s,e;
	cout<<"输入会议总数:"<<endl;
	cin>>n;
	cout<<"输入会议的开始时间和结束时间,以空格分开:"<<endl;
	for(i=0;i<n;++i){
		cin>>s>>e;
		meet[i].beg=s;
		meet[i].end=e;
		meet[i].num=i+1;
	}
}
bool cmp(Meet x,Meet y){
	if(x.end == y.end)
		return x.beg > y.beg;
	return x.end < y.end;
}
void setMeet::solve(){
	sort(meet,meet+n,cmp); //对会议按结束时间排序
	cout << "排完序的会议时间如下:"<<endl;
	int i;
	cout <<"会议编号"<<"  开始时间 "<<" 结束时间"<<endl;
	for(i-0;i<n;i++){
		cout<<"  "meet[i].num <<"\t\t"<<meet[i].beg<<"\t"<<meet[i].end<<endl;
	}
	cout<<"————————————————————————————————————"<<endl;
	cout<<"选择的会议的过程:"<<endl;
	cout<<" 选择第"<< meet[0].num<<"个会议"<<endl;  //选中了一个会议
	ans=1;
	int last = meet[0].end; //记录刚刚被选中会议的结束时间
	for(i=1;i<n;++i){
		if(meet[i].beg>=last){ //如果会议i开始时间大于等于最后一个选中的会议的结束时间
			ans++;
			last = meet[i].end;
			cout << " 选择第"<<meet[i].num<<"个会议"<<endl;
		}
	}
	cout<<"最多可以安排"<<ans<<"个会议"<<endl;
}

int main(){
	setMeet sm;
	sm.init();  //读入数据
	sm.solve();  //贪心算法求解
	return 0;
}

未完待续,预知后事如何,请听下回分解。

猜你喜欢

转载自blog.csdn.net/qq_34624515/article/details/84583149
今日推荐