一直前进!贪心法

贪心法基础概念

贪心法就是遵循某种规则,不断贪心地选取当前最优策略的算法设计方法。不过根据实际经验来说,需要注意的是,贪心规则的选取很重要。
在这里插入图片描述

例题1 硬币问题

问题描述如下:
在这里插入图片描述
该例题的所遵循的贪心规则是“优先使用面值大的硬币”
给出的代码如下:

//硬币面值
const int V[6] = {1, 5, 10, 100, 500};

//输入
int C[6];	//每枚硬币的个数
int A;

void solve(){
	int ans = 0;
	
	for(int i = 5; i >= 0; i--){
		int t = min(A / V[i], C[i]);	//使用硬币i的枚数
		A -= t*V[i];
		ans += t;
	}
	printf("%d\n",ans);
}

例题2 区间调度问题

问题描述如下:
在这里插入图片描述
本题中采用的贪心规则是尽可能选取结束快的活动。注意一些本题中很容易犯的错。
在这里插入图片描述
在这里插入图片描述
给出的代码如下:

const int MAX_N = 100000;

//输入
int N,S[MAX_N],T[MAX_N];

//用于对工作排序的pair数组
pair<int,int> tv[MAX_N];

void solve(){
	//对pair进行字典序比较
	//为了让结束时间早的工作排在前面,把T存入first,S存入second
	for(int i = 0; i < N;i ++){
	itv[i].first = T[i];
	itv[i].second = S[i];
	}
	sort(itv,itv + N);
	
	//t是最后所选工作的结束时间
	itn ans = 0; t = 0;
	for(int i = 0; i < N; i++){
		if(t < itv[i].second){
			ans++;
			t = itv[i].first;
		}
	}
	
	printf("%d\n",ans);
}

例题3 字典序最小问题

问题描述如下:
在这里插入图片描述
在这里插入图片描述
对于本例来说,贪心策略比较明显,就是不断选取更小的那个字母加入到末尾。
给出的解决方案如下

//输入
int N;
char S[MAX_N + 1];

void solve(){
	//剩余的字符串为S[a],S[a+1],...,S[b]
	int a = 0, b = N - 1;

	while(a <= b){
		//将从左起和从右起的字符串比较
		bool left = false;
		for(int i = 0; a + i <= b; i++){
			if(S[a + i] < S[b - i]{
				left = true;
				break;
			}
			else if(S[a + i] > S[b - i]){
				left = false;
				break;
			}
		}
		if(left)putchar(S[a++]);
		else putchar(S[b--]);
	}
	putchar('\n');
}

例题4 直线上点问题

问题描述如下:
在这里插入图片描述
在这里插入图片描述

一开始在选择贪心策略的时候选择了按两点距离来选择,但是因为即使按照距离排序,但是选择左边的点还是右边的点是一个问题,所以改成了在某个点范围内能覆盖到的点的数量的排序,但是因为数量相同时不同的选择会带来不同的结果(虽然我当时以为能用统一选择最左边的来解决,不过可能还是有风险?因为我没有具体举例去看),所以卡在这里了

而书中给出的解决方案如下:
在这里插入图片描述
感觉是更高一级的思路,因为从最左边的点开始看,所以不需要担心更左边,而用该种方法选出来的点也变成了下一个的最左边的点,不需要担心更左边的问题。于是我们得到了问题的答案。
给出的代码如下:

int N,R;
int X[MAX_N];

void solve(){
	sort(X,X + N);
	int i = 0, ans = 0;
	while(i < N){
	//s是没有被覆盖的最左边的点的位置
	int s = X[i++];
	//一直向右前进直到距s的距离大于R的点
	while(i < N && X[i] <= s + R) i++;

	//p是新加上标记的点的位置
	int p = X[i - 1];
	//一直向右前进直到距p的距离大于R的点
	while(i < N && X[i] <= p + R) i++;
	
	ans++;
	}
	printf("%d\n",ans);
}


例题5 木板切割问题

问题描述如下:
在这里插入图片描述
自己的思路是:因为每次切割木板的开销都是切完两侧长度相加,而当时脑子里有的切法只是按照固定长度一个一个切,所以这样的话本身切出来的长度加起来是一定的,就是题目中要求的长度,而剩下的长度的计算当然是越小越好,那么其实也就是每次选择最大的切就好。但是通过实验书上的这个例子之后发现这种方法有问题。但是是怎么想到利用二叉树来求解的呢?
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

参考这个博客 ,这里选择二叉树可以用霍夫曼树的思想来思考,真是奇妙。不过是不是霍夫曼在遇到值相同的节点的时候有一些讲究还是这题不算经典的霍夫曼算法,因为我一开始用我理解的霍夫曼算法去跑上面这个例子的时候还是得到了错误的答案。以及这个地方的贪心思想还是一开始累加最小的木板,到了后面所积累起来的和就最小。
这里就直接将代码截图了
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值