【算法笔记】DAY7_4.4贪心

简单贪心

求解的每一步都是最优,那么结果就是最优的。严格的贪心需要想出来这个最优的策略,但是简单贪心一般可以看出来,顺着题目的意思想就可以。

P1181 数列分段 Section I

思路:能分段就分段,不能的话就下一个。

#include<iostream>
using namespace std;
int main() {
	int n, m, a;
	cin >> n >> m;
	int sum = 1;
	int space = 0;
	for (int i = 1; i <= n; i++) {
		cin >> a;
		if (space + a <= m) {
			space += a;
		}
		else {
			sum++;
			space = a;
		}
	}
	cout << sum;
	return 0;
}

 P1094 [NOIP2007 普及组] 纪念品分组

思路:排序,最大的找最小的,如果不行就自己分组。这个代码的巧妙之处是,定位l最小,r最大,++/--实现在数组里的左右移动。

代码:

#include<iostream>
#include<algorithm>
using namespace std;
int a[30005];
int main() {
	int w, n;
	cin >> w;
	cin >> n;
	int sum = 0;
	for (int i = 0; i < n; i++) {
		cin >> a[i];
	}
	sort(a, a + n);
	int l = 0, r = n - 1;
	while (l <= r) {
		if (l == r) {
			sum++; break;
		}
		if (a[r] + a[l] <= w) {
			r--; l++;
			sum++;
		}
		else {
			r--;
			sum++;
		}
	}
	cout << sum;
	return 0;
}

贪心背包

不是动态规划,是不完全背包。

P1208 [USACO1.3] 混合牛奶 Mixing Milk

思路:

就一直买单价最低的牛奶就可以。

#include<iostream>
#include<algorithm>
using namespace std;

struct milkseller {
	int p;
	int a;
}milk[5005];

bool cmp(milkseller a, milkseller b ) {
	return a.p < b.p;
}

int main() {
	int n, m;
	cin >> n >> m;
	int money = 0;
	for (int i = 1; i <= m; i++) {
		cin >> milk[i].p >> milk[i].a;
	}
	sort(milk + 1, milk + m + 1, cmp);
	for (int i = 1; i <= m; i++) {
		if (milk[i].a <= n) {
			money += milk[i].a * milk[i].p;
			n = n - milk[i].a;
		}
		else {
			money +=n * milk[i].p;
			n = 0; break;
		}
	}
	cout << money;
	return 0;
}

区间贪心

P1803 凌乱的yyy / 线段覆盖

思路1:求不重合的线段,第一种方法是始终找最大的左端点:让线段排序的时候左端点从大到小排,相同左端点的让其右端点从小到大排序,这样的话可以避免重合;然后从右边往左边排序,在不会部分重合的情况下比较下一条线段的右端点和现在的最左端点就可以。

为什么这样可以避免重合呢?因为这时候是从右边向左边排序,每一次左端点最大的此时是最靠近的所以选最大的左端点的,选右端点小的存粹是为了代码计算的时候一次就成,如果此时最短的线段都放不下,那就不用想相同左端点长度更长的了。

#include<iostream>
#include<algorithm>
using namespace std;
const int MAX = 1000005;
struct inteval{
	int x,y;
}I[MAX];
bool cmp(inteval a, inteval b) {
	if (a.x != b.x) return a.x > b.x;//左端点从大到小
	else return a.y <= b.y;//右端点从小到大
}
int main() {
	int n;
	cin >> n;
	for (int i = 0; i < n; i++) {
		cin >> I[i].x >> I[i].y;
	}
	sort(I, I + n, cmp);
	int ans = 1; int lastx = I[0].x;
	for (int i = 1; i < n; i++) {
		if (lastx >= I[i].y) {
			ans++;
			lastx = I[i].x;
		}
	}
	cout << ans;
	return 0;
}

思路2:找最小的右端点。cmp比较函数先让右端点不同的从小到大排序,如果右端点相同的话,就让左端点从小到大排序,这样也可以避免重合。去掉重合后的思路和思路1一样。

这时候为什么没有重合?现在是从左向右排序,每次最左边的一定是右端点最小的,此时右端点最小的是最靠近左边的。找此时左端点小的,是因为想先找短的线段,毕竟固定了一个右端点之后,上一个右端点到现在的右端点之间智能放一条线段,最短的能放就够了。

代码如下:

#include<iostream>
#include<algorithm>
using namespace std;
const int MAX = 1000005;
struct inteval{
	int x,y;
}I[MAX];
bool cmp(inteval a, inteval b) {
	if (a.y != b.y) return a.y < b.y;//右端点从小到大
	else return a.x <= b.x;//左端点从小到大
}
int main() {
	int n;
	cin >> n;
	for (int i = 0; i < n; i++) {
		cin >> I[i].x >> I[i].y;
	}
	sort(I, I + n, cmp);
	int ans = 1; int lasty = I[0].y;
	for (int i = 1; i < n; i++) {
		if (lasty <= I[i].x) {
			ans++;
			lasty = I[i].y;
		}
	}
	cout << ans;
	return 0;
}

总结: 贪心是用来解决一类最优化问题,并希望由局部最优策略来推得全局最优策略的算法。贪心算法适用的问题一定满足最优子结构性质,即一个问题的最优解可以由子问题的最优解构造出来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值