跳转至

POJ-3069 Saruman's Army(贪心)

Description

Saruman the White must lead his army along a straight path from Isengard to Helm’s Deep. To keep track of his forces, Saruman distributes seeing stones, known as palantirs, among the troops. Each palantir has a maximum effective range of R units, and must be carried by some troop in the army (i.e., palantirs are not allowed to “free float” in mid-air). Help Saruman take control of Middle Earth by determining the minimum number of palantirs needed for Saruman to ensure that each of his minions is within R units of some palantir.

Input

The input test file will contain multiple cases. Each test case begins with a single line containing an integer R, the maximum effective range of all palantirs (where 0 ≤ R ≤ 1000), and an integer n, the number of troops in Saruman’s army (where 1 ≤ n ≤ 1000). The next line contains n integers, indicating the positions x*1, …, *xn of each troop (where 0 ≤ xi ≤ 1000). The end-of-file is marked by a test case with R = n = −1.

Output

For each test case, print a single integer indicating the minimum number of palantirs needed.

Sample Input

0 3
10 20 20
10 7
70 30 1 7 15 20 50
-1 -1

Sample Output

4
2

//POJ 3069
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

vector<int> sequence(1000);
int range, n;

int mark()
{
    int cnt = 0;
    int pos = 0;
    while (true) {
        int target = sequence[pos] + range; //下面去寻找最后一个不大于目标值的数
        while (pos < n && sequence[pos] <= target) {++pos;}
        ++cnt;
        if (pos >= n) break;
        pos = pos - 1;
        target = sequence[pos] + range; //寻找第一个大于目标值的数
        while (pos < n && sequence[pos] <= target) {++pos;}
        if (pos >= n) break;
    }

    return cnt;
}

int main()
{
    std::ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);

    while ((cin >> range >> n) && range != -1 && n != -1) {
        for (int i = 0; i < n; ++i) {
            cin >> sequence[i];
        }
        sort(sequence.begin(), sequence.begin() + n);
        cout << mark() << endl;
    }

    return 0;
}

对于整个序列,总可以找到一个没有标记的点,然后以此开始,为了让标记的点尽可能地少,那么就应该去寻找最后一个小于**当前坐标值+覆盖范围**的点,这时候会出现两种情况,第一种,到了序列地最后也没能找到,这时候肯定要++cnt,直接退出循环;如果没有到末尾并且找到了这样一个位置,那么就在这个位置放置标记点,也需要++cnt,所以两种情况地区别只在于多了一行判断。找到了一个标记点,然后去寻找下一个不在覆盖区域地点,仍然会有两种情况,第一种,到了末尾那么直接退出就好;第二种,找到了这样一个点,会发现此时和我们最初分析问题地情景是一致地,这样就形成了循环,也相当于验证了我们程序地正确性。程序只需要把上述分析内容转成代码即可。

注意地一点是,这里用了“寻找最后一个不大于目标值地数”,“寻找第一个大于目标值地数”,很容易联想到二分法里面的upper_bound(),之所以不采用是因为二分查找位置, 一次查找的效率是O(logn),如果最坏的情况下每个坐标点都不互相覆盖,那么时间复杂度就是O(nlogn),而这里的方法最坏情况也是O(n),所以最好不要采用二分的办法。

当然如果考虑到了因为我们在执行mark()函数前对数组排了序,时间复杂度已经是O(nlogn)了,那么是否可以采用二分来代替程序里面搜索下一个位置呢?最好也不要,因为如果题目变换一下,输入序列是有序的,时间卡的严格一点可能就不行了,所以保险起见还是不用二分来搜索位置。