zoukankan      html  css  js  c++  java
  • 【做题笔记】 P5051 【[COCI2017-2018#7] Timovi】

    本题所有程序默认开启O2,否则超时。这不是正解,但是比正解简洁明了,代码可读性强,但是不开O2超时。如有能力,建议学习正解。

    另:题面有一个地方不太清楚:当满足 (m<k) 时,若仍有剩余,要放到下一个队伍里去。


    根据题意,我们不难想到,设 (ans_i) 表示第 (i) 个队伍有多少人,那么,我们就不难想到一直放下去,知道满足条件为止。

    但是注意到当到达第 (n) 个队伍的时候要逆序(i.e. (n o (n-1) o ... o 2 o 1 o 2 o ... o (n-1) o n...))。所以我们记一个flag变量,当flag=0的时候,说明是正序,否则倒序。然后设 (now) 表示现在到了第 (num) 个队伍,若 (now=n) 则另flag=1,若 (now=1) 则令flag=0

    需要注意在flag变动时看一下当前 (m) 是否小于 (k)

    参考代码(91points):

    #include <cstdio>
    
    int now=1, n, k, m, ans[200010], flag;
    
    inline int read()
    {
        int s=0,w=1; char ch=getchar();
        while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
        while(ch>='0'&&ch<='9') s=s*10+ch-'0' , ch=getchar();
        return s*w;
    }
    
    int main(void) {
        n=read(), k=read(), m=read();
    
        while(1) {
            if(now==n) {
                flag=1; //标记从现在开始就是倒序
                ans[now] += k; //加上
                m -= k; //减去
                now--; //倒序则不断把now减一,模拟了倒序的过程
                if(m<k) { //判断是否满足条件
                    ans[now] += m;
                    break; 
                }
                continue; //不用继续进行后面的操作了
            }
    
            else if(now==1) {
                flag=0;
                ans[now] += k;
                m -= k;
                now++;
                if(m<k) {
                    ans[now] += m;
                    break; 
                }
                continue;
            } //同上
    
            if(m<k) { //这里仍然要判断
                ans[now] += m;
                break; 
            }
    
            ans[now] += k;
            m -= k;
    
            if(flag) --now; //如果当前标记为倒序,则now--
            else ++now; //否则now++
    
        }
    
        //printf("QAQ
    ");  //QAQ
    
        for(int i=1;i<=n;i++)
            printf("%d ", ans[i]);
    
        return 0;
    }
    

    91分,不是因为TLE,而是因为WA!为啥呢?

    上面那份代码的hack数据(感谢@metaphysis 的提供):

    3 2 4
    

    这份数据正确的结果应该是2 2 0,但是上面那份程序跑出了2 0 2

    为什么会出现这样的情况呢?原因出在判断 (now) 那里,以第一个判断为例:

    if(now==n) {
        flag=1; 
        ans[now] += k;
        m -= k; 
        now--;
        if(m<k) {
            ans[now] += m;
            break; 
        }
        continue;
    }
    

    假如说正好分配完,没有多余,那么上面那份代码就出现了一个致命错误。为了帮助理解,我们演示一遍(人脑模拟gdb 233):

    现在now=n, k=2, m=0
    flag=1(没啥好说的)
    ans[now] += k(看到问题了么?明明已经没有人了,但是还是加了一个不存在的 (k)
    m-=k (问题严重了!现在 (m=-2) !)
    now-- (导火索,现在去前面一个队伍了)
    if(m<k) { (进入)
    ans[now] += m (注意,这里是把前面一个队伍加上一个人(相当于减去))
    break; (跳出while循环)

    我们发现,我们把 (ans_n) 平白无故地加上了两个人;而把 (ans_{n-1}) 平白无故地减去了两个人(总人数虽然没变,但是答案已经错了)。

    如果您可以明白上面那套东西,解决办法也就很自然了:提前判断是否满足条件,就可以解决这个问题。

    最终AC代码(再次提醒开了O2

    #include <cstdio>
    
    int now=1, n, k, m, ans[2000010], flag;
    
    inline int read()
    {
        int s=0,w=1; char ch=getchar();
        while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
        while(ch>='0'&&ch<='9') s=s*10+ch-'0' , ch=getchar();
        return s*w;
    }
    
    int main(void) {
        n=read(), k=read(), m=read();
    
        while(1) {
            if(now==n) {
    
                if(m<k) {
                    ans[now] += m;
                    //printf("Now we need break and now The 'm'=%d
    ", m);
                    break; 
                } //提前判断即可。
    
                flag=1;
                ans[now] += k;
                //("now=%d,OK! a[%d]=%d
    ", n, n, ans[n]);
                m -= k;
                now--;
    
                continue;
            }
    
            else if(now==1) {
    
                if(m<k) {
                    ans[now] += m;
                    //printf("Now we need break and now The 'm'=%d
    ", m);
                    break; 
                }
    
                flag=0;
                ans[now] += k;
                //printf("now=1,OK! a[1]=%d
    ", ans[1]);
                m -= k;
                now++;
    
                continue;
            }
    
            if(m<k) {
                ans[now] += m;
                //printf("Now we need break and now The 'm'=%d
    ", m);
                break; 
            }
    
            ans[now] += k;
            m -= k; 
            //printf("now=%d,OK! a[%d]=%d
    ", now, now, ans[now]);
            if(flag) --now;
            else ++now;
        }
    
        //printf("QAQ
    ");
    
        for(int i=1;i<=n;i++)
            printf("%d ", ans[i]);
    
        return 0;
    }
    
  • 相关阅读:
    用grunt搭建自动化的web前端开发环境-完整教程
    redis 使用
    ubuntu系统安装redis
    redis resque消息队列
    linux下常用的命令
    介绍Mina的TCP的主要接口(一)
    Mina框架详解——简单的TCPClient
    Mina框架详解——简单的TCPServer
    Mina框架知识解析
    RabbitMQ新手入门Helloworld
  • 原文地址:https://www.cnblogs.com/BlueInRed/p/12617595.html
Copyright © 2011-2022 走看看