[NOIP2007 普及组] 纪念品分组
题目背景
NOIP2007 普及组 T2
题目描述
元旦快到了,校学生会让乐乐负责新年晚会的纪念品发放工作。为使得参加晚会的同学所获得 的纪念品价值相对均衡,他要把购来的纪念品根据价格进行分组,但每组最多只能包括两件纪念品, 并且每组纪念品的价格之和不能超过一个给定的整数。为了保证在尽量短的时间内发完所有纪念品,乐乐希望分组的数目最少。
你的任务是写一个程序,找出所有分组方案中分组数最少的一种,输出最少的分组数目。
输入格式
共 $n+2$ 行:
第一行包括一个整数 $w$,为每组纪念品价格之和的上限。
第二行为一个整数 $n$,表示购来的纪念品的总件数 $G$。
第 $3\sim n+2$ 行每行包含一个正整数 $P_i$ 表示所对应纪念品的价格。
输出格式
一个整数,即最少的分组数目。
样例 #1
样例输入 #1
100
9
90
20
20
30
50
60
70
80
90
样例输出 #1
6
提示
$50\%$ 的数据满足:$1\le n\le15$。
$100\%$ 的数据满足:$1\le n\le3\times10^4$,$80\le w\le200$,$5 \le P_i \le w$。
贪心
重在证明,虽然AC了证明还是模糊
贪心策略:
1.首先对所有物品的价值从小到大进行排序
2.用两个指针,一个指向前端,另一个指向后端;
3.最小的匹配最大的时候分组数最小
4.当 $a_i + a_j > w$时,j--;
说明后面数太大了,只能独立作为一个组
5.当 $a_i + a_j <= w$ 时,i++, j--;
两个数作为一个组
证明:
分情况进行讨论:
首先贪心得到的解是一种可行方案,而最后的最优解是所有可行方案中的最小值,
因此可以证明出 最优解 <= 贪心得到的解
存在最优解S ,他不是按照贪心步骤得到的;
1.$a_i + a_j > w$ 时,由于从小到大进行了排序 $a_i$ 是当前最小的,说明$a_j$不能与其他任何$a_k$为一组,$i < k < j $,因此$a_j$只能独立分组;
2.$a_i + a_j <= w$ ,而最优解中并不是$a_i与a_j$ 一组;
(1)$a_j$ 是独立分组的,并且$a_i$也是独立分组的时候,由于$a_i + a_j <= w$,将它俩合并为同一个组,这时分组数-1,这里可以证明出最优解 >= 贪心得到的解
到这一步我们可知贪心得到的解就是最优解。
进一步说明:
已知 $i < k < j$,
(2)$a_j$ 单独一组,$a_i 与 a_k$ 一组,将$a_i$与$a_k4拆分,再把$a_i$与$a_j$合并,这是分组数不变
(3)$a_j与a_k$ 一组,$a_i$单独一个组,这时候交换$a_i,a_k$, 分组数仍不变
C++ 代码
#include <bits/stdc++.h>
using namespace std;
const int N = 3e4 + 10;
int w,n;
int a[N];
int ans;
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> w >> n;
for(int i = 1; i <= n; i++){
cin >> a[i];
}
sort(a + 1, a + 1 + n);
int i = 1,j = n;
while(i <= j){
if(a[i] + a[j] > w) {
ans ++; //单独作为一个组
j--;
}
else{
ans ++; //两件物品一起作为一个组
i++;
j--;
}
}
cout << ans << endl;
return 0;
}