小韦老师@NOIP 普及组-2007-纪念品分组

小韦老师@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;
  1. 输入 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,组数加 1。
	if (a[l] + a[r] <= w) {  // 若两者之和小于等于 w,可以分成一组 
		l++;  // 左边的指针(下标)加 1 
		r--;  // 右边的指针(下标)减 1
		cnt++;  // 计数器加 1,a[l] 和 a[r] 作为一组,组数加 1 
	}
  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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值