UVa #11134 Fabled Rooks (例题8-4)

86 篇文章 0 订阅

书中的提示很有帮助,将这道题拆成两个一维空间分开进行判断,那么就变成了贪心法的区间重叠问题


设车的摆放位置为 [a,b]。我的算法是,将所有的区间按照 b 的值从小到大排列,b相同的按照 a 从小到大排列。然后从排好序的第一个区间(即a、b最小)开始选择摆放位置。


选摆放位置的时候,从左向右扫描,一旦有某一列暂时空着(没有其他的车摆在这一列),则马上摆放在这里,然后继续摆下一个车。如果某一辆车从左向右扫描到尽头,还没有找到空着的位置,那么此组数据无解


为什么这样的贪心法可以获得正确解:

因为区间已经按照a、b排好序,所以上下相邻的两个区间的左右边界一共有几种情况:

1、b1 < b2, a1 < a2

2、b1 < b2, a1 = a2

3、 b1 < b2, a1 > a2

4、 b1 = b2, a1 < a2

5、 b1 = b2, a1 = a1

(下面所说的最左端的点,全部指代从左侧开始往右搜索,第一个没有被其他车占着的点)

对于1和4,很显然第一个区间选择最左端的点是合理的,否则将会造成浪费(后面所有的区间都不再能选到那个点,那个点将永远空着)

对于2和5,如果第一个区间不选择最左端的点,那么第二个区间可以选择最左端的点,这和我们的解法结果相同。

对于3,第一个区间的选点对第二个区间并没有直接的影响,但是却可能会对第三个、第四个...造成影响(假如将第二个区间拿走,则第一个和第三个区间可能形成1、2、4、5的情况。如果第一个区间不选择最左端的点的话,可能会对第三个区间的选点会造成浪费)。

按照这样的算法,对于所有已经摆好的区间,我们可以确定整体上已经是最紧凑的状态,没有一点浪费。所以如果到了某一个区间,发现所有的位置都无法摆放,则可以果断返回无解,因为之前摆好的车再怎么调整也无法腾出空位。


要注意的是,同样的算法不可以按照 a 的大小来排序。


举一个反例:

两个区间a和b的范围分别是 [4,9] 和 [5,8]。按照排序规则,a要排在b上面:

(a)   4 5 6 7 8 9
(b)     5 6 7 8


如果此时 1-7 都已经被其他的车占领,那么a区间会选择占据 8。区间b就会返回无解。但实际上a可以占9,而b占8


如果要按照a的大小排序的话,我感觉应该反向搜索,即每次摆放在最右端的可行位置。


不经意间居然做了一道带星号的例题。。我感觉也是撞大运了


Run Time: 0.026s

#define UVa  "LT8-4.11134.cpp"

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>

using namespace std;

struct Range {
    int l, r, order;
    Range(int a, int b, int c):l(a),r(b),order(c){}
    bool operator < (const Range& r2) const {
        return (r == r2.r) ? l < r2.l : r < r2.r;
    }
};

//Global Variables.
int n;
const int maxn = 5000 + 10;
vector<Range> restrict[2];
int ans[maxn][2], vis[maxn][2];     //[0] for row, [1] for col;



void print_ans() {
    for(int i = 0; i < n; i ++) {
        if(vis[i][0] != -1) ans[vis[i][0]][0] = i;
        if(vis[i][1] != -1) ans[vis[i][1]][1] = i;
    }

    for(int i = 0; i < n; i ++) {
        printf("%d %d\n", ans[i][1]+1, ans[i][0]+1);
    }
}

int solve() {
    for(int k = 0; k < 2; k ++) {
        for(int i = 0; i < n; i ++) {
            int l, r, order;
            l = restrict[k][i].l;
            r = restrict[k][i].r;
            order = restrict[k][i].order;
            while(l <= r && vis[l][k] != -1) {
                l++;
            }
            if(l > r) return 0;
            vis[l][k] = order;
        }
    }
    print_ans();
    return 1;
}


int main() {
    while(scanf("%d", &n) && n) {
        restrict[0].clear();
        restrict[1].clear();
        memset(ans, -1, sizeof(ans));
        memset(vis, -1, sizeof(vis));

        int a, b, c, d;
        for(int i = 0; i < n; i ++) {
            scanf("%d%d%d%d", &a, &b, &c, &d);
            Range colRange(a-1,c-1, i);                //ATTENTION. -1 applied.
            Range rowRange(b-1,d-1, i);
            restrict[0].push_back(rowRange);
            restrict[1].push_back(colRange);
        }
        for(int i = 0; i < 2; i ++) sort(restrict[i].begin(), restrict[i].end());

        if(!solve()) {
            printf("IMPOSSIBLE\n");
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值