Part 2.5 贪心

题单

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

思路

这个题贪心显而易见,即先买便宜的,再买贵的,直到满足需求,这样节省开支。,所以时间复杂度为O(min {n, mlogm})

代码

#include <bits/stdc++.h>
using namespace std;
const int maxm = 5005;
int n, m, ans;
struct farmer 
{
	int p, a;
} f[maxm];
bool cmp (farmer a, farmer b) 
{
	return a.p < b.p;
}
int main ()
{
	scanf ("%d %d", &n, &m);
	for (int i = 1; i <= m; i++) 
	{
		scanf ("%d %d", &f[i].p, &f[i].a);
	}
	sort (f + 1, f + m + 1, cmp);
	int p = 1;
	while (n > 0) 
	{
		if (n >= f[p].a) 
		{
			n -= f[p].a;
			ans += (f[p].a * f[p].p);
		}
		else 
		{
			ans += (n * f[p].p);
			n = 0;
		}
		p ++;
	}
	printf ("%d", ans);
	return 0;
}

P4995 跳跳!

思路

这个题要求我们的是跳完所有的石柱并使体力的消耗值最大,其中两个石柱之间的高度差越大,体力消耗越多,那么,我们可以对h[i](所有柱子的高度)进行由大到小的排序,使得h[1] > h[2] >……> h[n];那我们肯定首先跳上排完序的第一个柱子以消耗最多的体力,然后跳到第n个柱子,然后跳到第2个柱子,以此类推,我们发现当n等于偶数时,ans = h[1] ^ 2 + (h[1] - h[n]) ^ 2 + (h[2] - h[n]) ^ 2 + (h[2] - h[n - 1]) ^ 2 + …… ,然后我们发现h[1]^2与其他项不太和谐,怎么办呢?观察我们的第3项和第四项,发现我们可以设一个h[n + 1] = 0!这样,当n为偶数的时候,ans = 对(h[i] - h[n + 2 - i]) ^ 2 + (h[i] - h[n + 1 - i]) ^ 2求和,其中i从1到(n / 2)。当n为奇数时,同理,可推出另一个式子,这里不在赘述。

代码

#include <bits/stdc++.h>
using namespace std;
const int maxn = 305;
int n, h[maxn], temp[maxn];
long long ans;
void merge (int l, int r) 
{
	if (l == r) 
	{
		return;
	}
	int mid = (l + r) / 2;
	merge (l, mid);
	merge (mid + 1, r);
	int i = l, j = mid + 1, p = l; 
	while (i <= mid && j <= r) 
	{
		if (h[i] > h[j]) 
		{
			temp[p ++] = h[i ++];
		}
		else 
		{
			temp[p ++] = h[j ++];
		}
	}
	while (i <= mid) 
	{
		temp[p ++] = h[i ++];
	}
	while (j <= r) 
	{
		temp[p ++] = h[j ++];
	}
	for (int i = l; i <= r; i++) 
	{
		h[i] = temp[i];
	}
}
int main ()
{
	scanf ("%d", &n);
	for (int i = 1; i <= n; i++) 
	{
		scanf ("%d", &h[i]);
	}
	merge (1, n);
	h[n + 1] = 0;
	if (n % 2 == 0) 
	{
		for (int i = 1; i <= n / 2; i++) 
		{
			ans += (h[i] - h[n + 2 - i]) * (h[i] - h[n + 2 - i]) + (h[i] - h[n + 1 - i]) * (h[i] - h[n + 1 - i]);
		}
	}
	else 
	{
		for (int i = 1; i <= (n - 1) / 2; i++)
		{
			ans += (h[i] - h[n + 2 - i]) * (h[i] - h[n + 2 - i]) + (h[i] - h[n + 1 - i]) * (h[i] - h[n + 1 - i]);
		}
		int i = (n + 1) / 2;
		ans += (h[i] - h[n + 2 - i]) * (h[i] - h[n + 2 - i]);
	}
	printf ("%lld", ans);
	return 0;
}

收获

这个题推出答案的表达式极大的简化了代码

P2672 [NOIP2015 普及组] 推销员

思路

这个题让我们求的是最多积累的疲劳值,所以我们首先考虑贪心。既然是贪心,那我们一般会对某个变量或多个变量运算形成的整体为对象,进行排序。那么我们这个题是对A[i]还是S[i]进行排序呢?若对S[i]进行由小到大的排序,假设最远的一户人家的下标为a,那么答案就是2 * s[a] + A[a] + A[p1] + A[p2] + ……+A[p(x - 1)],其中A[pi]为A[1]到A[a]中的前(x - 1)大,而每次的前x-1大都不好求,故对s[i]进行排序会使题较难做;若对A[i]进行排序,则只需要计算至多两个s[i]之间的运算,故选择对A[i]进行排序。 那我们对A[i]进行由大到小的排序,当他要访问x户人家时,那么我们可能的答案就是sum (A[i]),i从1到x + max {s[i]},i = 1, 2, ……x。那么有没有可能通过舍弃重新排序后的第x户人家在后面的人家中找一户人家来换取更远的距离来弥补A的减小,并使结果更大?是由可能的,即sum (A[i]),i从1到x + max {s[i] * 2},i = 1, 2, ……x < sum (A[i]),i从1到x-1 + max {2 * s[i] + a[i]},i = x + 1, x + 2, n是由可能成立的 那么我们我们舍弃前x家中的两家,从后面再找两家来使答案有可能吗?若后面的两家中没有一家可以在前x家丢弃一家再找他作为最远家的情况下使答案更优,则显然问题不可能;若后面的两家中有一家可以在前x家丢弃一家再找他,让他作为最远家的情况下使答案更优,那么剩下的一家也只是把前x家中的一个较大的A给替换掉了,所以也不可能。故不可能换两家,更别说两家以上了。 对于sum(A[i]),我们采取前缀和;对于max {s[i] * 2},i = 1, 2, ……x和 max {2 * s[i] + a[i]},i = x + 1, x + 2, ……n,我们用两个数组存储就好了。故O(nlogn)。

代码

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1000005;
int n;
long long ans, q[maxn], h[maxn], pre[maxn];
struct w 
{
	long long a, s;
} t[maxn];
bool cmp (w c, w b) 
{
	return c.a > b.a;
}
int main ()
{
	scanf ("%d", &n);
	for (int i = 1; i <= n; i++) 
	{
		scanf ("%lld", &t[i].s);
	}
	for (int i = 1; i <= n; i++) 
	{
		scanf ("%lld", &t[i].a);
	}
	sort (t + 1, t + n + 1, cmp);
	for (int i = 1; i <= n; i++) 
	{
		pre[i] = pre[i - 1] + t[i].a;
	}
	for (int i = 1; i <= n; i++) 
	{
		q[i] = max (q[i - 1], 2 * t[i].s);
	}
	for (int i = n; i >= 1; i--) 
	{
		h[i] = max (h[i + 1], 2 * t[i].s + t[i].a);
	}
	for (int i = 1; i <= n; i++)
	{
		printf ("%lld\n", max (pre[i] + q[i], pre[i - 1] + h[i]));//对于 pre[i - 1] + h[i]可能会有一点疑问,那就是如果h[i]代表后面那家不够远怎么办?这个不用多想,因为这种情况下,他肯定小于pre[i] + q[i],肯定不会成为答案。
	}
	return 0;
}

收获

这个题的思路真的很值得反复考虑、复习

P1199 [NOIP2010 普及组] 三国游戏

思路

我和计算机都是为了胜利这一目标,故与博弈论有点相似
但是这个题的题解真的太难写了,直接丢个写的比较好的题解吧

代码

#include <bits/stdc++.h>
using namespace std;
const int maxn = 505;
int a[maxn][maxn], n;
bool cmp (int x, int y) 
{
	return x > y;
}

int main ()
{
	scanf ("%d", &n);
	for (int i = 1; i < n; i++) 
	{
		for (int j = i + 1; j <= n; j++) 
		{
			scanf ("%d", &a[i][j]);
			a[j][i] = a[i][j];
		}
	}
	int ans = -2147483647;
	for (int i = 1; i <= n; i++) 
	{
		sort (a[i] + 1, a[i] + n + 1, cmp);
		ans = max (ans, a[i][2]);
	}
	printf ("1\n%d", ans);
	return 0;
}

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

思路

根据要求(最小的分组数目)可知,我们应尽量充分利用每组纪念品价格之和的上限w,即两个物品的价格之和应尽量接近w。首先,我们对价格进行由小到大的排序,那我们肯定不会让小的两个进行配对,因为两个小的配对,可能会浪费w;那我们也不会让两个大的进行配对,因为即使价格之和小于w,当分组达到一定地步时,就会出现两个小的进行配对的情况,也会浪费w。那么我们选择一个大的和一个小的进行配对,首先肯定是最大的和最小的进行配对,如果他俩之和<=w,在他俩在一组;否则,最大的单独在一组,因为第二小的与最大的之和也会>w,剩下的以此类推即可,直到分完组。

代码

#include <bits/stdc++.h>
using namespace std;
int w, n, p, ans;
priority_queue <int, vector <int>, greater <int> > qi;
priority_queue <int, vector <int>, less <int> > qii;
int main ()
{
	scanf ("%d %d", &w, &n);
	for (int i = 1; i <= n; i++) 
	{
		scanf ("%d", &p);
		qi.push (p);
		qii.push (p); 
	}
	while (n > 0) 
	{
		if (qi.top() + qii.top() > w) 
		{
			ans ++, n --;
			qii.pop();
		}
		else 
		{
			ans ++, n-= 2;
			qi.pop(), qii.pop();
		}
		if (n == 0) 
		{
			break;
		}
	}
	printf ("%d", ans);
	return 0;
}

收获

重新学习了优先队列

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值