You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
|
|
|
|
## [$AcWing$ $430$. 纪念品分组](https://www.acwing.com/problem/content/description/432/)
|
|
|
|
|
|
|
|
|
|
(贪心,排序) $O(nlogn)$
|
|
|
|
|
|
|
|
|
|
直觉上讲,分组的时候应该尽可能让每一组的价值之和大一些。
|
|
|
|
|
|
|
|
|
|
由此得到如下算法:
|
|
|
|
|
|
|
|
|
|
将所有物品按价值排序;
|
|
|
|
|
从小到大枚举每个物品,每次给当前物品找一个价值尽可能大的且总价值没有超过上限的“同伴物品”,将两个物品分在一组,这一步可以使用双指针算法优化到 $O(n)$。
|
|
|
|
|
这样求出的组数就是最小值。
|
|
|
|
|
|
|
|
|
|
下面给出证明。这里可以使用 **调整法**:
|
|
|
|
|
|
|
|
|
|
假设最优解的分组方式和由上述算法得到的分组方式不同。那么我们考虑从后往前第一个分组不同的数,记为 $a$,假设由上述算法得到的“同伴物品”是 $b$,那么:
|
|
|
|
|
|
|
|
|
|
如果在最优解中,$a$ 单独一组,那么可以直接将 $b$ 从原组中取出,和 $a$ 放在一起,这样并没有增加组数;
|
|
|
|
|
如果在最优解中,$a$ 和 $c$ 放在一起,由上述算法可知,$c≤b$,那么我们可以将 $b$ 和 $c$ 所在位置交换,交换后两组的价值之和都没有超过上限,且这样也没有增加组数。
|
|
|
|
|
|
|
|
|
|
因此通过上述调整,我们可以在不增加组数的情况下,将最优解的分组方式调整成和上述算法相同,且这样的调整方式可以一直进行下去,直到两个方案相同为止。
|
|
|
|
|
因此我们可以在不增加组数的情况下,将最优解的方案调整成上述算法得到的方案,因此上述算法可以得到最优解。
|
|
|
|
|
|
|
|
|
|
时间复杂度
|
|
|
|
|
排序是算法瓶颈,因此时间复杂度是 $O(nlogn)$。
|
|
|
|
|
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
#include <bits/stdc++.h>
|
|
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
const int N = 30010;
|
|
|
|
|
|
|
|
|
|
int n, m;
|
|
|
|
|
int w[N];
|
|
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
|
// m:每组纪念品价格之和的上限
|
|
|
|
|
// n:购来的纪念品的总件数
|
|
|
|
|
cin >> m >> n;
|
|
|
|
|
for (int i = 0; i < n; i++) cin >> w[i]; // 表示所对应纪念品的价格
|
|
|
|
|
// 将价格由小到大排序
|
|
|
|
|
sort(w, w + n);
|
|
|
|
|
|
|
|
|
|
int res = 0;
|
|
|
|
|
// 双指针对撞
|
|
|
|
|
for (int i = 0, j = n - 1; i <= j; j--) {
|
|
|
|
|
if (w[i] + w[j] <= m) i++;
|
|
|
|
|
res++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cout << res << endl;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
```
|