算法分析与设计---贪心算法(期末复习版)

一、贪心法设计思想

1.贪心法

贪心法在解决问题的策略上一般只根据当前已有的信息做出选择,这个选择就不会改变。即贪心法并不是从整体最优考虑,它所做出的选择只是在某种意义上的局部最优。

  • 贪心算法对许多问题能产生整体最优解,如单源最短路径问题,最小生成树问题
  • 并不能总获得总体最优解,但通常能获得近似最优解。

2.设计思想

使用贪心选择策略,把一个简单的问题分解为一系列较为简单的局部最优选择,每一步选择都是对当前解的一个扩展,直到获得问题的完整解。

3.贪心法求解的问题特征

(1)最优子结构性质(最优性原理)

问题的最优解包含子问题的最优解。

(2)贪心选择性质

指问题的全局最优解可以通过局部最优得到,即存在一个最优解是以贪心选择开始的

4.贪心选择性质证明

(1)假设问题的一个整体最优解;

(2)证明可以将这个整体最优解修改成从贪心选择开始,此时,原问题就简化成为一个相似的、规模较小的子问题;

(3)用数学归纳法证明,通过过每一步的贪心选择,最终可得到问题的整体最优解。

例:

1.活动安排问题。

 贪心选择性证明:

  • 证明总存在一个以贪心选择开始的最优活动安排方案(以a1开始的最优解

设E={a1,a1....an}为所有活动的集合,且E中元素按活动时间递增排序,即a1是最早开始的活动。

设集合A是E的子集,且A是一个最优解,A中第一个元素为K(A中活动也按时间递增排序)

①k=a1,即A的第一个活动为a1,故A是以贪心选择为开始的最优解;

②k!=a1,设集合B=(A-{k})∪{a1},即用a1替换掉活动k,又因为a1的结束时间比k早,且A中元素相容,则B中元素也相容。又A的元素个数和B中元素个数相同,故B也是最优解。所以B是一个以贪心选择开始的最优解。

  • 每一步所做的贪心选择都将原问题简化为一个更小的与原文题相同形式的子问题。(含最优性原理证明

在贪心选择了a1之后,原问题简化为对与a1相容的活动进行活动安排的子问题。

即若A是原问题的一个以贪心选择开始的最优解,则A‘=A-{a1}是活动安排问题E’={i包含于E,Si>fa1}的一个最优解。

证明:如果能找到E‘的一个最优解B’,它包含的活动比A‘多,则将a1加入B’中,其所含的活动数比A多,这与A是最优解相矛盾

故每一步所做的贪心选择都将原问题简化为一个更小的与原问题相同形式的子问题。

2.最优装载问题

贪心选择性质证明:

第一步:总存在一个以贪心选择开始的最优解

设W={w1,w1...w1}为需要装载的集装箱。按重量递增的顺序进行排列,则w1是最轻的。

设A是W的子集且A是最优解。设A的第一个元素为k

①k=w1,则A是以贪心选择开始的最优解;

②k!=w1,设集合B=(A-{k})∪{w1},即用w1代替k。由于w1的重量小于k重量,故B中元素相容;又因为B中元素与A中元素数量相同,所以B也是一个最优解。故B是一个以贪心选择开始的最优解。

综上,总存在一个以贪心选择开始的最优解。

第二步:证明每做一步贪心选择,都将问题转化为规模更小的与原问题相同形式的子问题。

证明:贪心选择了w1后,原问题简化为将剩余的集装箱尽可能装入船上的问题。

即若A是原问题的一个以贪心选择开始的最优解,则A’=A-{w1}是问题E’={wi属于W,i>1}的一个最优解。

若存在集合B‘,其装载的物品数量比A’多,则若将w1加入B‘中得到的B比A中装载货物数量更多,与A是最优解相矛盾,所以。。。。。

二、贪心法求解过程

  1. 建立数学模型描述问题;
  2. 把求解的问题分成若干个子问题;
  3. 对每一个子问题进行贪心选择求解,得到子问题的局部最优解;
  4. 把子问题的局部最优解合成原来解问题的一个解。

 1.最优装载问题

#include<iostream>
#include<algorithm>

using namespace std; 
const int N = 1000000;
double v[N];//货物的体积数组

int main()
{
	int ans = 0;// 已经装入船只的货物数量
	int tmp = 0;//装入船只货物的总体积
	int V; //货车最多容纳的货物总体积
	int n;//货物的数量
	
	cout<<"输入货车体积及货物个数:"<<endl;
	cin>>V>>n;
	cout<<"输入货物的体积:"<<endl;
	for(int i = 0; i < n; i++){
		cin>>v[i];
	}
	//需要得到更多的货物,贪心策略即每次装入最轻的货物 
	sort(v, v+n);//货物体积进行降序排列
	for(int i = 0; i < n; i++){
		tmp += v[i];
		if(tmp <= V)ans++;
		else break;
	}
	 cout<<"货车能装入的货物最大数量为"<<ans<<' '<<endl;
	return 0;
}

2.最小生成树

(1)Prim算法

策略:每次都选到下一顶点权最小的边

#include<iostream>
#include <cstring>
using namespace std;
#define N 100
#define Max 9999

void Prim(int n,int c[][N])
{
	int lowcost[N],closest[N];
	bool s[N];
	s[1]=true;  //从第一个点开始 
	//初始化 
	for(int i=2;i<=n;i++)
	{
		lowcost[i]=c[1][i];	//记录到当前节点i最小距离
		closest[i]=1;		//记录到当前节点i最小距离的节点1
		s[i]=false;			//未更新 
	}
	for(int i=1;i<n;i++)
	{
		//找最小距离,一个一个把点加进去 
		int min=Max;
		int j=1;
		//找到距离最小的点j(第一步是和1最小的点) 
		for(int k=2;k<=n;k++)
		{
			if((lowcost[k]<min)&&(!s[k]))
			{
				min=lowcost[k];
				j=k;
			}
		}
		cout<<closest[j]<<' '<<j<<endl;
		
		//标记j已被找到 
		s[j]=true;
		
		//在这里, lowcost[k]为结点k的最小距离,
		//有可能是之前的点,也可能是新加入的结点j
		//所以这里需要更新 
		for(int k=2;k<=n;k++)
		{
			if((c[j][k]<lowcost[k])&&(!s[k]))
			{
				lowcost[k]=c[j][k];
				closest[k]=j;
			}
		}
	}
}



int main()
{
	int point,edgenum,x,y,len,c[N][N];
	memset(c,Max,sizeof(c));
	cin>>point>>edgenum;
	for(int i=1;i<=edgenum;i++)
	{
		cin>>x>>y>>len;
		c[x][y]=len;
		c[y][x]=len;
	}
	Prim(point,c);
	return 0;
}
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值