1. 程式人生 > 實用技巧 >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();
		}
	}
}