【杭电1050】Moving Tables

题目描述直达:点击打开链接

题目看似不难,用贪心的思想就可以完成。但有若干小陷阱。

1、输入数据<s, t>可能会存在s大于t的情形。

2、<1,3> <4,5>要分两组完成,而<1, 2> <3, 5>只需要一组。

这些,起初就想到了,先看我的思路吧。

大致是这样的,首先需要为房间号x做一个(x+1)/2的映射,创建一个标记数组flag[200][201],第一维表示组号,第二维表示对应的过道占用情形,举个简单例子,flag[0][3]=1表示第1组第3号过道占用,凡是经过房间号5、6的都不能在这一组同时完成。对输入数据<s, t>依次扫描,然后跟现有的每一组标记做对比,如果可以的话,就放到该组完成,否则,跟下一组做对比,这样进行下去。如果直到最后一组也不能同时完成的话,就需要新加一组。

代码看起来有点臃肿。但是自我感觉思路应该没有问题的。可事实证明,只是自己想当然而已。因为提交之后总是WA。

#include <stdio.h>

int flag[201][201];
int main()
{
    int nCases, n, i, cnt;
    int s, t, p, is_ok, done;
    
    scanf("%d", &nCases);
    while (nCases--) {
        cnt = 0;
        memset(flag, 0, sizeof(flag));
        scanf("%d", &n);
        while (n--) {
            scanf("%d %d", &s, &t);
            if (s>t) {
                s ^= t; t ^= s; s ^= t;
            }
            i = 0; done = 0;
            while (!done && i<cnt) {
                p = (s+1)/2; is_ok = 1;
                while (is_ok && p<=(t+1)/2) {
                    if (flag[i][p]==1)
                        is_ok = 0;
                    p++;
                }
                if (is_ok) {
                    p = (s+1)/2;
                    while (p<=(t+1)/2) {
                        flag[i][p] = 1;
                        p++;
                    }
                    done = 1;
                }
                i++;
            }
            if (!done) {
                p = (s+1)/2;
                while (p<=(t+1)/2) {
                    flag[cnt][p] = 1;
                    p++;
                }
                cnt++;
            }
        }
        printf("%d\n", cnt*10);
    }
    return 0;
}

想啊想啊想破脑袋。终于想到一个测试用例把前面的思路推翻。<1, 8> <4, 15> <17, 25> <13, 19>
<1, 8>放第一组;<4, 15>放第二组;<17, 25>放第一组;<13, 19>放第三组
好,结果是要分3组完成。可事实上呢,两组就可以完成了,<1, 8> <13, 19>放第一组,<4, 15> <17, 25>放第二组。
为什么会这样呢?在处理每组输入数据时,我是 依次扫描之前的分组情况,导致最终方案会跟输入数据的顺序有关。正确的做法应该是,扫描之前的分组情况,选择加入一个“最合适”的分组。
后来,参考网上大牛们的代码。发现一个不错的思路。
标记数组从二维降到一维,flag[201]。flag[i]表示过道第i个位置的占用总数。最终,占用总数最多的那一个位置就是所需要的分组数目。

#include <stdio.h>

int flag[201];
int main()
{
    int nCases, n, i, cnt;
    int s, t;
    
    scanf("%d", &nCases);
    while (nCases--) {
        cnt = 0;
        memset(flag, 0, sizeof(flag));
        scanf("%d", &n);
        while (n--) {
            scanf("%d %d", &s, &t);
            if (s>t) {
                s ^= t; t ^= s; s ^= t;
            }
            for (i=(s+1)/2; i<=(t+1)/2; i++)
                flag[i] += 1;
        }
        cnt = flag[1];
        for (i=2; i<201; i++) {
            if (cnt<flag[i])
                cnt = flag[i];
        }
        printf("%d\n", cnt*10);
    }
    return 0;
}



 

总结:原始问题该怎么转化?转化后更要确保问题是等价的。动手扣锭之前要思考清楚!



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值