其他分享
首页 > 其他分享> > POJ 3069 Saruman's Army

POJ 3069 Saruman's Army

作者:互联网

题目如下:

一个游戏:在一条直线上有N个糖果。第i个糖果的位置是X[i]。从这N个糖果中选择若干个,把他们标记起来。对于每一个糖果,在和它本身相距为R的区域内必须要有标记的糖果(本身带有标记的糖果,就可以认为和它相距为0的地方有一个糖果被标记)。在满足这个条件的情况,最后如果有a个糖果被标记,编写程序使a最小化。


Input

输入的测试文件将包含多个样例。 每个测试样例第一行有两个数据,整数R(其中0≤R≤1000)和整数N(其中1≤N≤1000)。 下一行包含N个整数,指示每个糖果的位置X[1],…,X[N](其中0≤X[i]≤1000)。当RN-1时,输入结束。


Output

对于每组输入数据,输出一个数,代表a的最小值。


Sample Input

0 3

10 20 20

10 7

70 30 1 7 15 20 50

-1 -1


Sample Output

2

4


解题思路:

这道题是经典的区间贪心问题,题目中给的每一个点都有其相应的范围[x-R,x+R]。并且,在每个点距离为R的区域里必须有一个带有标记的点。(包括该点本身也可以进行标记)在满足这种条件的情况下,希望能为尽可能少的点去添加标记,求最少可标记的点。


那么,根据这道题,我们应该如何制定我们的贪心策略呢?


首先,既然是要求尽可能少的点去添加标记。我们假设:从最左边考虑,当前点为x、要添加标记的点为y。那么y一定是在当前点x所引出范围中更靠右的点。因为y只有越靠右,该范围能添加标记的点也就越少。越靠左,该范围能添加标记的点也就越多。不懂?再提个假设:共有点:1、3、4,当前点为1,范围为5。那么,当前点的范围就是[-4,6]。既然我们想让这个范围中添加标记的点尽可能少,那么这个添加标记的点就是4而不是3。因为,我们是从左往右进行考虑的,假设我们将3添加了标记,那么4肯定也能添加标记。这样的话,当前范围中添加标记的点就不是尽可能少的了。


这样的话,我们的大致思路就确定下来了。由于我们是从左往右进行考虑的,且测试实例中的输入点可能不是从左往右依次有序的。所以在进行标记点之前,我们应该把输入的点进行从小到大排序。(这里用sort函数就行了)


排完序之后,我们应该从左往右进行查看。对于每一个查看的点,我们都应该找出该点范围内距离最靠右的点。只有这样的话,才能保证每个范围内标记的点必须只有一个。


但是,光上面的结论还不足以完成这道题目。设想一下,如果有的点已经是标记的点了,那么我们还用去找这个点所引出的区间中最靠右的点了吗?由于之前已经说过,在每个点距离为R的区域里必须有一个带有标记的点。(包括该点本身)所以,如果有的点已经被标记了,那么就不用去找该点所引出的区间中最靠右的点了。而应该跳过这个区间,去找下一个待标记的点。


代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int R, N;                        //定义整数R和整数N
int X[1001];                     //定义存储每个点的数组
void greedy();                   //定义贪心算法的函数

void greedy() {
	int i=0;
	int sum = 0;                  //代表插入点的个数
	int s, p;                     //定义起始点和插入点的变量
	while (i < N) {
		s = X[i++];                         //将起始点进行赋值(可以将i++换成i,只不过后面循环的次数+1了而已)
		while(i<N && X[i] <= s + R) {       //一直向右前进,直到距s的距离大于R的点(i<N的原因就是为了防止下标越界)(如果将i<N去掉的话,某些输入实例就会越界)(可以参考如下实例:R=10 N=4 {1,2,3,4})
			i++;
		}
		p = X[i - 1];                       //代表设置标记点
		while (i<N && X[i] <= p + R) {      //标记点所引出的区间应当跳过(继续向右前进,直到距p的距离大于R的点)
			i++;
		}
		sum++;
	}
	cout << sum << endl;
}

int main() {
	int i;
	while (scanf("%d %d", &R, &N) != EOF) {
		if (R ==-1 && N==-1) {
			return 0;
		}
		else {
			for (i = 0; i < N; i++) {
				scanf("%d", &X[i]);
			}
			sort(X, X + N);             //将输入的数进行从小到大排序。
			greedy();
		}
	}
}

标签:标记,int,靠右,添加,POJ,Saruman,3069,糖果,范围
来源: https://www.cnblogs.com/gao79135/p/14261788.html