算法学习(四)贪心算法(重要)

贪心算法

1、基本思想

1.1、概述

我们知道求解最优化问题的算法一般包括一系列的求解步骤,每一步骤都要在许多选择里面做一个决策,然后才能得到问题的一个解。
贪心算法(Greedy Algorithm)总是在每一步骤中做出最优的决策,希望通过一系列的局部最优决策,从而获得问题的全局最优解。
在这里插入图片描述
日常例子如:打牌、股票投资、安排课程如何安排使得资源利用率最大化等。
如图下的最短路径问题,两幅图表示了前一步的选择与后一步是否独立
在这里插入图片描述

1.2、活动选择问题

动态规划法实现

1、问题描述:在这里插入图片描述
在这里插入图片描述
输入:任务的个数、每个任务的起始终止时间
分析:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

递归:

#include<iostream>
#include<cstdio>
using namespace std;
 
int c[100][100]={0};//c[i][j]
int Maxactivities_dp(int i,int j,int (*a)[2]){
	if(i>=j){
		c[i][j]=0;
		return 0;
	}	
	int m=0;
	for(int k=i+1;k<j;k++){
		if(a[i][1]<=a[k][0]&&a[k][1]<=a[j][0]){//满足就计算是否为最大值 
			c[i][k]=Maxactivities_dp(i,k,a);
			c[k][j]=Maxactivities_dp(k,j,a);
			int temp = c[i][k]+c[k][j]+1;
			if(temp>m)
				m = temp;
		}
		if(c[i][j]<m)
			c[i][j]=m;
	}
	return m;
	 
}
//打印选择的活动,利用了递推公式
void PrintSelectActivitise(int i,int j,int (*a)[2]){
	if(j-i==1)//没有活动
		return;
	for(int k=i+1;k<j;k++){
		if(a[i][1]<=a[k][0]&&a[k][1]<=a[j][0]){
			if(c[i][k]+c[k][j]+1==c[i][j])//满足递推关系则表示选择最优活动是ak
				PrintSelectActivitise(i,k,a);
				cout<<"A"<<k<<" ";
				PrintSelectActivitise(k,j,a);	
				break;//注意一次选择里面只选一个k 
		}
	} 
} 

int main(){
	int n;
	cout<<"活动个数:"<<endl;
	cin>>n; 
	
	cout<<"请输入"<<n<<"个活动(如3 5表示3点到5点活动):"<<endl; 
	int a[n+2][2];
	a[0][0]=-1;
	a[0][1]=0;//假想的活动
	 
	for(int i=1;i<n+1;i++){
		cin>>a[i][0]>>a[i][1];
	} 
	a[n+1][0]=25;
	a[n+1][1]=26; //假想的活动
	
	//对活动的结束时间进行升序排序
	for(int i=1;i<n+2;i++){//冒泡排序
		for(int j=0;j<n+2-i;j++)
			if(a[j][1]>a[j+1][1]){
				int temp = a[j+1][1];
				a[j+1][1]=a[j][1];
				a[j][1]=temp;
				int temp1 = a[j+1][0];
				a[j+1][0] = a[j][0];
				a[j][0]= temp1;
			}
	} 	
	cout<<endl<<Maxactivities_dp(0,n+1,a)<<endl;
	PrintSelectActivitise(0,n+1,a); 	
	
	return 0;
} 

在这里插入图片描述
迭代:

#include<iostream>
#include<cstdio>
using namespace std;
 
int c[100][100]={0};//直接所有都初始化为0 
int Maxactivities_dp(int start,int end,int(*a)[2]){
	if(start>=end||end-start==1)
		return 0;
	//for(int m=0;m<end;m++){//初始化边界条件 
	//	c[m][m]=0;//因为活动按照结束时间排序的 
	//	c[m][m+1]=0;	
	//}
	for(int i=start;i<end;i++){
		for(int j=i+2;j<end+1;j++){ 
			int max=0;
			for(int k=i+1;k<j;k++){
				if(a[k][0]>=a[i][1]&&a[k][1]<=a[j][0]){
					int temp = c[i][k]+c[k][j]+1;
					if(max<temp)
						max = temp;					
				}
			}
			if(c[i][j]<max)
				c[i][j]=max; 	
		}
	}
	return c[start][end];
	
}

在这里插入图片描述

贪心算法实现

贪心算法分析:
从上面的分析,我们有:
在这里插入图片描述
在这里插入图片描述
这个就是贪心策略。代码实现:
递归:

#include<iostream>
using namespace std;
int record[100]={0};//记录选择的活动 
int Maxactivities_greedy(int start,int end,int (*a)[2],int *record){
	int m=start+1;
	while(m<end&&a[m][0]<a[start][1])
		m = m+1;//寻找当前结束时间最小的am,
	if(m<end){
		record[m]=1;
		return 1+Maxactivities_greedy(m,end,a,record);
	}
	else return 0;
}

在这里插入图片描述
迭代法:

//迭代法
int Maxactivities_greedy(int start,int end,int(*a)[2],int *record){
	int i=start+1;//第一个活动经过排序必然是最快结束的
	record[i] = 1;//记录 
	int max=1;
	for(int m=i+1;m<end;m++){
		if(a[i][1]<=a[m][0]){
			max +=1;
			record[m]=1;//记录 
			i = m;	
		}
	}
	return max; 
} 

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

体会贪心算法的设计步骤

步骤:
在这里插入图片描述
贪心选择:
在这里插入图片描述
在这里插入图片描述

2、实战例子

2.1、背包问题(0-1背包、分数背包)

:0-1背包问题无法用贪心算法解决,可以用动态规划实现;分数背包问题可以。

分数背包问题:
在这里插入图片描述
假设输入形式:输入物品个数 n,背包容量C,输入物品的序号,重量,价值。
如:
3 4
1 1 2
2 2 5
3 4 3
输出
选择:1,2,总价值:7

贪心算法最重要的是选择正确的贪心策略。在这个问题里,如果我们用贪心算法,明显我们可以想到的贪心策略有如下三种:

  • 1、每次选择价值最大的物品,这可以尽可能快的增加背包的总价值,但是,虽然每一步选择获得了背包价值的极大增长,但背包容量却可能消耗的太快,使得装入背包的物品个数减少,从而不能保证目标函数达到最大。
  • 2、每次选择重量最轻的物品,因为这可以装入尽可能多的物品,从而增加背包的总价值。但是,虽然每一步选择使背包的容量消耗的慢了,但背包的价值却没能保证迅速的增长,从而不能保证目标函数达到最大。
  • 3、以上两种贪心策略或者只考虑背包价值的增长,或者只考虑背包容量的消耗,而为了求得背包问题的最优解,需要在背包价值增长和背包容量消耗二者之间寻找平衡。正确的贪心策略是选择单位重量价值最大的物品。
    在这里插入图片描述

知道了贪心策略,我们就可以设计算法了:

//n物品个数;w背包总容量;a2维数组,存储物品编号、物品重量、物品价值(按照物品的价值率降序排序) 
void SelectMaxValue(int n,int W,int (*a)[3],int *record){
	float value = 0;
	int w;//w为实际装多少。 
	for(int i=0;i<n;i++){
		if(W<a[i][1])//最大价值率物品 
			w=W;
		else
			w=a[i][1];
		record[i]=a[i][0];//记录选择的物品编号
		value+=a[i][2]*1.0/a[i][1]*w;
		W-=w;
		if(W==0)//装满了 
			break;
	}
	cout<<"选择物品编号:";
	for(int i=0;i<n;i++){
		if(record[i]!=0)
			cout<<record[i]<<" ";
	}
	cout<<endl<<"最大价值:"<<value; 
} 

在这里插入图片描述
因为这里可以选择装一部分,也就是我们的第三种贪心算法可以保证装满背包。

0-1背包问题

因为0-1背包问题要么装全部,要么一点不装的限制。我们的三种贪心算法都无法保证最后的结果是最优的。

  • 第一种贪心,肯定不正确
  • 第二种贪心,肯定不正确
  • 第三种贪心,因无法保证背包被装满而导致单位背包空间利用率下降而不正确。

我们用动态规划来解决0-1背包问题:
在这里插入图片描述

#include<iostream>
using namespace std;
n物品个数;V背包容量;a2维数组,存储物品编号、物品重量、物品价值 
void SelectMaxValue(int n,int V,int(*a)[3],int (*f)[100]){
	for(int i=1;i<=n;i++){
		for(int j=1;j<=V;j++){
			if(j<a[i-1][1])//装不下第i件 
				f[i][j]=f[i-1][j];
			else{//装得下第i件,不代表一定要装才价值大 
				int temp1 = f[i-1][j];//不装 
				int temp2 = f[i-1][j-a[i-1][1]]+a[i-1][2];//装 
				if(temp1>temp2)
					f[i][j]=temp1;		
				else{
				 	f[i][j]=temp2;  
				}
			}
		}
	}
	cout<<"选取的编号是:";

	for(int i=n,j=V;i>0;i--){
		if(f[i][j]==f[i-1][j-a[i-1][1]]+a[i-1][2]){
			cout<<i<<" ";
			j-=a[i-1][1];
		}	
	
	}
    cout << endl << "选取的最大价值是:" << f[n][V] << endl;
 
		
} 
int main(){
	int n,W;
	cout<<"输入物品个数、背包总容量:"<<endl;
	cin>>n>>W;
	int a[n][3];
	int f[100][100]={0};//默认 
	cout<<"输入物品编号、物品重量、物品价值:"<<endl;
	for(int i=0;i<n;i++){
		cin>>a[i][0]>>a[i][1]>>a[i][2];
	} 
	SelectMaxValue(n,W,a,f); 
	return 0;
	
}

在这里插入图片描述
特殊的0-1背包问题
可用贪心法求解,因为其价值率排行是有规则的。
在这里插入图片描述

2.2、哈夫曼编码

在这里插入图片描述
在这里插入图片描述
参考代码实现:哈夫曼树与哈夫曼编码及代码实现
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
从零开始学习贪心算法需要掌握以下几个点: 1. 贪心算法的定义:贪心算法是指在问题求解过程中,总是做出当前看来最好的选择,而不考虑整体最优解。贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。 2. 贪心算法的解题步骤:建立数学模型描述问题,将问题分解为若干个子问题,对每个子问题求解得到局部最优解,将局部最优解合成原问题的最优解。 3. 贪心算法动态规划的关系:贪心算法动态规划的一种特例。贪心算法不需要知道一个节点所有子树的情况,只需要选择当前问题的最优解路径,一直走到底即可。而动态规划需要自底向上构造子问题的解。 4. 活动选择问题贪心算法的一个经典例子。在活动选择问题中,需要在同一天使用同一个教室举行多个活动,要求选择尽可能多的活动不冲突地进行。 综上所述,从零开始学习贪心算法,要理解贪心算法的定义和特点,掌握贪心算法的解题步骤,了解贪心算法动态规划的关系,并通过经典例子如活动选择问题来实践和加深对贪心算法的理解。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [从0开始学贪心算法](https://blog.csdn.net/weixin_45024585/article/details/107515191)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [leetcode338-leetcode:从零开始](https://download.csdn.net/download/weixin_38750644/19950444)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [从零开始学贪心算法](https://blog.csdn.net/qq_32400847/article/details/51336300)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雨夜※繁华

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值