【算法】六、贪心算法

6.0引入    

0-1背包问题:有一个背包,最多能承载100斤的重量,现在有5个物品,其重量和价值如下表所示。如何装物品,使得在不超重的情况下背包中的物品价值最大?(每件物品只有一个,且物品不能分割)

我们可以选用不同的贪心策略:

1.每次挑选价值最大的物品装入

2.每次挑选重量最小的物品装入

3.每次选取单位重量价值最大的物品装入

通过对于每种策略的演算,我们可以发现,贪心法有时候得不到最优解。

例如我们选取第一个和第二个策略时,是无法得到最优解的。

6.1概述

贪心法的基本思路:从问题的某一个初始解出发,根据某种贪心策略,向给定的目标推进,每一步都做一个当时看似最佳的贪心选择,不断地将问题变为更小的相似子问题,并期望通过所做的局部最优选择产生出一个全局最优解。

也就是说贪心法不从整体最优上加以考虑,所做出的仅是在某种意义上的局部最优解。

适合贪心法求解的问题必须具备以下两要素:
1.贪心选择性质:所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。

2.最优子结构性质:原问题能转化为一组同类的子问题,原问题做贪心选择之后,子问题处理方法同原问题类似,并且原问题的最优解包含着它子问题的最优解。

6.2贪心法示例

6.2.1汽车加油问题

明确贪心策略:走到自己能走到并且离自己最远的那个加油站,在那个加油站加满油后,同从起点出发一样,再按照同样的方法贪心选择下一个加油站。

int main() {
	int i,num = 0,s=0; //num加油次数;s加满油后已行驶的公里数
	int a[N+2]= {0,1,2,3,4,5,1,6,6};
	int b[N+1]= {0}; //加油站加油最优解b1~bN
	for(i = 1; i <=N+1 ; i++)
		if(a[i] > K) {
			printf("no solution\n");
			return 0;
		}
	for(i = 1,s = 0; i < N+1; i++) {
		s += a[i];
		if(s + a[i+1] > K) {
			num++;
			b[i]=1;
			s = 0;
		}
	}
	return 0;
}

6.2.2活动安排问题

明确贪心策略:

1.优先选择具有最早开始时间的活动

2.优先选择持续时间最短的活动

3. 优先选择具有最早结束时间的活动

我们选择第三个贪心策略,因为这样每一次的选择,可以使得余下的时间最大化,可以安排更多的活动。

struct Action { //活动的类型声明
	int b; //活动起始时间
	int e; //活动结束时间
	bool operator<(const Action &s) const { //重载<关系函数
		return e<=s.e; //用于按活动结束时间递增排序
	}
};
int n=11;
Action
A[]= {{0},{1,4},{3,5},{0,6},{5,7},{3,8},{5,9},{6,10},{8,11},
	{8,12},{2,13},{12,15}
}; //下标0不用
//求解结果表示
bool ans[MAX]; //标记选择的活动
int Count=0; //选取的兼容活动个数
void solve() { //求解最大兼容活动子集
	memset(ans,0,sizeof(flag)); //初始化为false
	sort(A+1,A+n+1); //A[1..n]按活动结束时间递增排序
	int preEnd=0; //前一个兼容活动的结束时间
	for (int i=1; i<=n; i++) //扫描所有活动
		if (A[i].b>=preEnd) { //找到一个兼容活动
			ans[i]=true; //选择A[i]活动
			preEnd=A[i].e; //更新preend值
		}
}

6.2.3蓄栏保留问题

这个题与活动安排问题类似,也是找出最大兼容活动子集,找到的最大兼容活动子集个数就是最少蓄栏个数。

 

struct Cow { //奶牛的类型声明
	int no; //牛编号
	int b; //起始时间
	int e; //结束时间
	bool operator<(const Cow &s) const { //重载<关系函数
		if (e==s.e) //结束时间相同按开始时间递增排序
			return b<=s.b;
		else //否则按结束时间递增排序
			return e<=s.e;
	}
};
int n=5;
Cow A[]= {{0},{1,1,10},{2,2,4},{3,3,6},{4,5,8},{5,4,7}};
//下标0不用
//求解结果表示
int ans[MAX]; //ans[i]表示第A[i].no头牛的蓄栏编号
void solve() { //求解最大兼容活动子集个数
	sort(A+1,A+n+1); //A[1..n]按指定方式排序
	memset(ans,0,sizeof(ans)); //初始化为0
	int num=1; //蓄栏编号
	for (int i=1; i<=n; i++) { //i、j均为排序后的下标
		if (ans[i]==0) { //第i头牛还没有安排蓄栏
			ans[i]=num; //第i头牛安排蓄栏num
			int preEnd=A[i].e; //前一个兼容活动的结束时间
			for (int j=i+1; j<=n; j++) { //查找一个最大兼容活动子集
				if (A[j].b>preEnd && ans[j]==0) {
					ans[j]=num; //将兼容活动子集中活动安排在num蓄栏中
					preEnd=A[j].e; //更新结束时间
				}
			}
			num++; //查找下一个最大兼容活动子集,num增1
		}
	}
}

 6.2.4股票买卖问题

 1.若只能买卖一次,且无手续费

 

int main() {
	int prices[]= {0,3,9,2,5,4,7,1};
	int n=sizeof(prices)/sizeof(int)-1;
	int buy=1,sell=1,k=1,profit=0;

	for(int i = 2; i <= n; i++) {
		if(prices[i]-prices[k]>profit) { //今天是否卖出
			profit = prices[i]-prices[k];
			buy=k;
			sell=i;
		}
		if(prices[i] < prices[k]) k=i; //今天的价格是否更低
	}
	printf("buy=%d,sell=%d,profit=%d\n",buy,sell,profit);
	return 0;
}

2.若可以多次买入或卖出,无手续费

 

#include<stdio.h>
#define MAX 50
int prices[]={0,23,18,22,35,16,8,4,30,35};
int buy[MAX]={0};
int sell[MAX]={0};
int n=sizeof(prices)/sizeof(int)-1;
int profit=0;

void Yuce(){
	int buy=0,sell=0,profit=0;
	int i;
	for(i=1;i<=n;i++){
		if(i==1){
			if(prices[1]<prices[2])
			buy=i;
		}
		else if(i==n){
			if(prices[n]>prices[n-1]&&buy)
			sell=n;
		}
		else{
			if(prices[i]>prices[i+1]){
				if(buy)sell=i;
			}
			else if(!buy)buy=i;
		}
		if(buy&&sell){
			printf("buy at %d day,sell at %d day,profit is %d\n",buy,sell,prices[sell]-prices[buy]);
		profit+=prices[sell]-prices[buy];
		buy=sell=0;
		}
	}
	printf("total prices is %d",profit);
}

int main(){
	Yuce();
} 

6.2.5跳跃游戏

 

 

 

#include<stdio.h>
#define MAX 50

int num[]={0,3,5,2,7,6,9,6,2,4,9,2,3,4,8,7,3,15,6,7,4};
int n=sizeof(num)/sizeof(int) -1;
int arr[MAX]={0};

void find(){
	int longest=1,flag=0,sum=0;
	int i,j;
	for(i=1;i<=n;i++)
	arr[i]=i+num[i];
	
	for(i=1;i<=n;i++){
		if(longest>=n){
			printf("from %d to %d\n",i,n);
			sum++;
			flag=1;
			break;
		}
		
		if(i>longest)break;
		
		int max=i;
		for(j=i;j<=arr[i];j++){
			if(arr[j]>arr[max])
			max=j;
		}
		if(arr[max]>longest){
			longest=arr[max];
			sum++;
			printf("from %d to %d\n",i,max);
			i=max;
			i--;
			continue;
		}
	}
	if(flag)printf("total jump %d times\n",sum);
	else printf("cannot arrive\n");
}

int main(){
	find();
} 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

噶炜123

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

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

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

打赏作者

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

抵扣说明:

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

余额充值