poj 1083 & poj 1328

1083这道题是:给定x轴上一堆区间,想把它们分成若干组,在同一组内的区间要求两两都不能有重叠,问最少有几组。

1328这道题是:给定x轴上一堆区间,想把它们分成若干组,在同一组内的区间要求两两都重叠(组内所有区间至少有一个公共覆盖点),问最少有几组。

看起来很像,且都可以用贪心做。

对于1083,先把所有奇数端点+1,使之变成偶数,当然顺带把(起点,终点)里较小的那个放前面,这样就把输入变成了一堆区间。

一开始,我想任选一个区间开始,把所有跟他兼容的区间划到他那组,并把它们从区间集里删掉。看起来好像挺对,问题是有多个区间跟本区间兼容而那多个区间之间不兼容时,就不知道怎么选了,选最大的兼容集合是不能解决问题的。

后来,经朋友提示,可以这么做,把所有区间按结束时间排序,结束早的排前面。开始只有一个组,把区间一个个往组里放,如果当前区间能跟某个组已有的区间兼容,就把他放到那个组里,如果不能把他放到任何一个组里,就新开一个组。唯一需要注意的就是,如果一个区间可以放到多个组,怎么选。选结束时间最晚的一组。如果把它放到结束时间早的组里,可能就导致后面本来能跟结束时间早的组兼容的区间放不进来了。

后来看讨论,发现还可以把所有区间按开始时间排序,开始早的排前面。同样的把区间一个个往组里放,如果当前区间能跟某个组兼容,就随便把他放到一个能兼容的组,如果所有组都不能兼容,就新开一个组。能把某区间随便放到一个能兼容的组是因为,后面来的区间开始时间都比他晚,所以后面任何一个本来能放进组里的区间,还是能放进某区间没放的组里。这比上一个方法实现上还省事。

讨论中还说,可以把直线分成200份,计算每份被占用了多少次,(一份)占用次数的最大值就是最终要求的组数。首先,容易知道至少需要这么多组。然后,通过贪心算法的过程,如果某区间跟所有组都不兼容,一定是所有组都占用了包含区间开始点的一小段。这说明绝不会有某点占用数还不到组数,就新开一组的情况。所以最多需要这么多组。这个方法的巧妙之处就在于,他给出了最少有几组,却没给出分组方案,所以比贪心算法更简单。

按结束时间排序的代码:

#include <cstdio>
#include <iostream>
#include <utility>
#include <algorithm>
#include <vector>
using namespace std;

const int MAXN = 1000;
int groups[MAXN];
int indexes[MAXN];
struct Range {
    int left;
    int right;
} ranges[MAXN];

struct mycomp {
    bool operator() (int x1, int x2) {
        return ranges[x1].right < ranges[x2].right;
    }
} mycomp_object;

int main() {
    int t, n;
    scanf("%d", &t);
    while (t--) {
        scanf("%d", &n);
        int start, end;
        for(int i = 0; i < n; ++i) {
            scanf("%d %d", &start, &end);
            if (start % 2 != 0) start += 1;
            if (end   % 2 != 0) end   += 1;
            if (start > end) swap(start,end);
            ranges[i].left = start;
            ranges[i].right = end;
            indexes[i] = i;
        }
        sort(indexes, indexes+n, mycomp_object);
        int glen = 0;
        for (int i = 0; i < n; ++i) {
            Range cur = ranges[indexes[i]];
            int max_right = 0;
            int max_idx = -1;
            for (int j = 0; j < glen; ++j) {
                if (groups[j] < cur.left && groups[j] > max_right) {
                    max_right = groups[j];
                    max_idx = j;
                }
            }
            if (max_idx < 0) groups[glen++] = cur.right;
            else groups[max_idx] = cur.right;
        }
        printf("%d\n", 10*glen);
    }
    return 0;
}

对于一个结构体数组,对下标排序的方法是跟别人学来的,以后记着继续这么用。

1328这道题:对于每一个小岛的坐标,容易求出以这个点为圆心,d为半径的圆与x轴的两个交点,如果雷达设在两点之间,就能覆盖这个岛。如果没交点,则无解,返回-1. 只要两个区间有交集,就可以把雷达架在交集里,同时覆盖两个岛。所以希望在同一处相交的区间越多越好。

把这些区间按起始点排序后,如果第一个区间跟第二个区间没交集,那么一定需要单独用一个雷达去覆盖第一个区间。如果有交集,那可以再往下看,看这两个区间的交集的最右端,也就是结束较早的区间的右端,是不是跟第三个区间相交。如果没交集,那一定需要单独用一个雷达去覆盖第一和第二个区间,如果有交集,就可以再往下看,直到相交的部分与下一个区间没交集为止,这时分配一个雷达给前几个区间。

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <string>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;

const int MAXN = 1000;
int indexes[MAXN];
struct Range {
    float left;
    float right;
} ranges[MAXN];

struct mycomp{
    bool operator() (int x, int y) {
        return ranges[x].left < ranges[y].left;
    }
} mycompobject;

int main() {
    int t = 0, n, d;
    while (scanf("%d %d", &n, &d) == 2) {
        if (!n && !d) break;
        ++t;
        bool solve = true;
        for (int i = 0; i < n; ++i) {
            int tmp_x, tmp_y;
            scanf("%d %d", &tmp_x, &tmp_y);
            if (tmp_y > d || !solve) {
                solve = false;
                continue;
            }
            float sq = sqrt((d+tmp_y)*(d-tmp_y));
            ranges[i].left = tmp_x-sq;
            ranges[i].right = tmp_x+sq;
            indexes[i] = i;
        }
        if (!solve) {
            printf("Case %d: -1\n", t);
            continue;
        }
        sort(indexes, indexes+n, mycompobject);
        int cnt = 1;
        float cur_right_most = ranges[indexes[0]].right;
        for (int i = 1; i < n; ++i) {
            if (cur_right_most < ranges[indexes[i]].left) {
                ++cnt;
                cur_right_most = ranges[indexes[i]].right;
            }
            else {
                cur_right_most = min(cur_right_most, ranges[indexes[i]].right);
            }
        }
        printf("Case %d: %d\n", t, cnt);
    }
    return 0;
}


用这题实验了一下,把printf和scanf改成cout和cin,会慢一点(慢100ms左右)。把数组改成vector空间占用也会涨一点点。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值