小韦老师@NOIP 普及组-2007-纪念品分组
题目:
描述
元旦快到了,校学生会让乐乐负责新年晚会的纪念品发放工作。为使得参加晚会的同学所获得的纪念品价值相对均衡,他要把购来的纪念品根据价格进行分组,但每组最多只能包括两件纪念品,并且每组纪念品的价格之和不能超过一个给定的整数。为了保证在尽量短的时间内发完所有纪念品,乐乐希望分组的数目最少。
你的任务是写一个程序,找出所有分组方案中分组数最少的一种,输出最少的分组数目。
输入
第 1 行包括一个整数 w,为每组纪念品价格之和的上限。
第 2 行为一个整数 n,表示购来的纪念品的总件数。
第 3~n+2 行每行包含一个正整数 pi (5 <= pi <= w),表示所对应纪念品的价格。
输出
仅一行,包含一个整数,即最少的分组数目。
输入样例1
100
9
90
20
20
30
50
60
70
80
90
输出样例1
6
提示
50% 的数据满足:1 <= n <= 15
100% 的数据满足:1 <= n <= 30000, 80 <= w <= 200
题解:
思路:
整体思路:
把纪念品的价格按照从低到高排列,然后用两个指针(其实是两个 int 型的变量,用来表示下标),每次选择当前价格最高与价格最低的最为一组,若符号条件,则左边的指针向右移动 1,右边的指针向左移动 1;如果不行就让价格最高的单独分一组,而价格最低再和价格次高作为一组进行判断。
这样贪心下来就能使分的组数最少。
例如价格最低的是 Min,价格最高的是 Max,若 Max + Min 是大于给定的数 w,则说明不能分成一组,若这个时候 Max 再去加其他的价格,只会使得结果更大,则肯定大于 w,所以就不能分成一组。所以这个时候 Max 要自己分一组。
具体步骤:
1.定义数组,用来存储纪念品的价格:
const int N = 3e4 + 10;
int a[N]; // 用来存储纪念品的价格
2.定义 2 个变量 w 和 n,并输入 w 和 n:
int w; // 每组纪念品价格之和的上限
int n; // 纪念品的个数
cin >> w >> n;
- 输入 n 个纪念品的价格:
for (int i = 1; i <= n; i++) { // 输入 n 个纪念品的价格
cin >> a[i];
}
4.用 sort 给数组 a (纪念品的价格)升序排序:
sort(a + 1, a + n + 1); // 将纪念品的价格升序排序(按照价格从低到高进行排列)
5.定义 3 个变量:
int l = 1; // 左边的指针(其实是表示下标的)
int r = n; // 右边的指针(其实是表示下标的)
int cnt = 0; // 计数器,用来记录组数
6.当左边的指针小于等于右边的指针时,做以下操作:
- 若两个指针指向的纪念品价格能组成一组,则左边的指针向右移动 1,右边的指针向左移动 1,组数加 1。
if (a[l] + a[r] <= w) { // 若两者之和小于等于 w,可以分成一组
l++; // 左边的指针(下标)加 1
r--; // 右边的指针(下标)减 1
cnt++; // 计数器加 1,a[l] 和 a[r] 作为一组,组数加 1
}
- 若两个指针指向的纪念品价格不能组成一组,右边的指针指向的纪念品自己是一组,组数加 1,
且将右边的指针向左移动 1 (左边的指针不动,等待下一次分组)。
else { // 若不能分成一组
r--; // // 右边的指针(下标)减 1 (左边的指针不变)
cnt++; // 计数器加 1,a[r] 自己作为一组
}
7.输出结果:
cout << cnt; // 输出计数器,即为最少的组数
思考:
1°如何证明这样的贪心方案能得到最优解呢?
2°这里的指针是指什么?怎样使用这样的指针?你会使用了麽?
完整代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 3e4 + 10;
int a[N]; // 用来存储纪念品的价格
int main() {
int w; // 每组纪念品价格之和的上限。
int n; // 纪念品的个数
cin >> w >> n;
for (int i = 1; i <= n; i++) { // 输入 n 个纪念品的价格
cin >> a[i];
}
sort(a + 1, a + n + 1); // 将纪念品的价格升序排序(按照价格从低到高进行排列)
int l = 1; // 左边的指针(其实是表示下标的)
int r = n; // 右边的指针(其实是表示下标的)
int cnt = 0; // 计数器,用来记录组数
while (l <= r) { // 当左边的指针小于等于右边的指针时,记得要有等号
if (a[l] + a[r] <= w) { // 若两者之和小于等于 w,可以分成一组
l++; // 左边的指针(下标)加 1
r--; // 右边的指针(下标)减 1
cnt++; // 计数器加 1,a[l] 和 a[r] 作为一组,组数加 1
} else { // 若不能分成一组
r--; // // 右边的指针(下标)减 1 (左边的指针不变)
cnt++; // 计数器加 1,a[r] 自己作为一组
}
}
cout << cnt; // 输出计数器,即为最少的组数
return 0;
}