贪心:保卫花园
题目:P2878 [USACO07JAN] Protecting the Flowers S - 洛谷
题目概述:每头牛都呆在数组的某个坐标上,并且每分钟会吃掉i朵花,问如何排序把牛牵回坐标0处,使花的损失最小。求最小吃掉花的数量。
思路:每一波吃掉花的数量 = 其他奶牛的总吃草量 * 这头选中的奶牛走到坐标0的时间。因此,最终结果其实就是确定“牵牛”的顺序。
因此,就是对所有的奶牛排序,最重要的就是找出排序规则。
推导规律(重要)
- 在确定好 (假设确定好) 顺序的序列中,拿出相邻的两个元素
- 如果交换这两个元素,对前面以及后面确定好顺序的序列的结果不造成影响
- 此时就可以根据这两个元素交换前后的结果,推导出排序的规则
执行1:拿出相邻的 奶牛,j,和一头奶牛,i,
执行2:如果这两头奶牛交换牵走的顺序,对 j 前面所有奶牛的总吃草量有没有影响?对 i 后面所有奶牛的总吃草量有没有影响? 没有影响。
- i 和 j 交换顺序,都不影响前面先吃,一直吃到 i 和 j ...... 对前面没有影响。
- 虽然i和j交换顺序,但i 和 j 吃草的总时间没有变,所以对后面也没有影响。
执行3:推导排序规则
把它们同时除以 dj di,可以变形为:tᵢ / dᵢ < tⱼ / dⱼ
这个不等式的含义是:任务
i
的“单位损失” (tᵢ / dᵢ
) 小于任务j
的“单位损失” (tⱼ / dⱼ
)。单位损失越大,紧急度越高。
总代码:
#include <iostream>
#include <algorithm>using namespace std;
typedef long long LL;
const int N = 1e5 + 10;int n;
LL sum = 0, ret = 0;struct node
{int t;int d;
}a[N];bool cmp(node& x, node& y)
{return x.t * y.d < y.t * x.d;
}int main()
{cin >> n;for (int i = 1; i <= n; i++) {cin >> a[i].t >> a[i].d;sum += a[i].d; //记录所有的吃草量}sort(a+1, a+1+n, cmp);for (int i = 1; i <= n; i++){sum -= a[i].d; //减去每回合的补救ret += a[i].t*2*sum;}cout << ret << endl;return 0;
}
时间复杂度为O(n log n) 。
复习sort函数:
cmp
函数接受两个参数(通常是相邻或需要比较的元素),并返回一个布尔值:
-
如果返回
true
:表示第一个参数 (x
) 应该排在 第二个参数 (y
) 之前。 -
如果返回
false
:表示第一个参数 (x
) 不应该排在 第二个参数 (y
) 之前(即y
应该排在x
之前,或者它们的顺序无关紧要)。
你可以把 cmp(x, y)
理解为回答一个问题:“为了让数组是我想要的样子,x
必须排在 y
前面吗?”
如果
x.t * y.d < y.t * x.d
为true
:函数返回
true
,告诉sort
函数:“是的,为了让数组有序,x
必须排在y
前面。”如果
x.t * y.d < y.t * x.d
为false
:函数返回
false
,告诉sort
函数:“不,x
不必排在y
前面。” (这意味着y
可能会被放到x
前面)。
模板:
想要的效果(对数组进行排序) | 你应该书写的 | 解释 |
---|---|---|
升序(从小到大) |
| 如果 |
降序(从大到小) |
| 如果 |
按结构体中某个字段升序 |
| 如果 |
按结构体中某个字段降序 |
| 如果 |