zoukankan      html  css  js  c++  java
  • 题解 P1982 【小朋友的数字】

    题目链接

    Solution [NOIP普及组2013] 小朋友的数字

    题目大意:求(max{f[i]}),其中(f[i]= max{f[j]+ d[j] ;| ;j < i})d[j]$表示区间[1,j]内的最大连续子段和

    动态规划


    ​ 分析:看了题目题目大意,这题显然还是挺水的吧.

    ​ 我们先看状态(d)怎么求:

    ​ 我们设(td[i])表示以(i)结尾的最大连续子段和,那么显然

    (td[i] = max(td[i - 1] + val[i],val[i])),(val[i])即第(i)个小朋友每人手上的数字

    ​ 然鹅我们要求的是([1,j])内的最大连续子段和

    ​ 所以(d[i] = max(d[i - 1],td[i]))这个显然还是很容易想到吧?

    ​ 解决了状态(d)以后,我们再来看状态(f)如何求.这题的状态显然是(O(n))的,可是如果(O(n))转移的话,复杂度会炸成(O(n ^ 2)),无法接受.那么快速转移有几种方法:

    • 数据结构,如线段树等(我可能数据结构学傻了,线段树打到一半发现做复杂了),可以做到(O(logn))转移,总复杂度(O(nlogn))
    • 临时变量 我们可以用一个临时变量(temp\_f)来表示(max{f[j] + d[j]; | ; 1 leq j < i}),然后直接(f[i] = temp\_f),(temp\_f = max(temp\_f,f[i] + d[i])),可以做到(O(1))转移,总复杂度(O(n))
      然后我们发现,状态(td),(d),(f)之间互不依赖,我们完全可以一个(for)循环来进行状态的转移.并且(i)个状态要么依赖第(i - 1)个状态,要么依赖区间([1,i - 1])内的极值,我们完全可以用临时变量将这些状态全部保存下来,然后程序就跑的飞快

    ​ 另外此题有坑点,会爆(long;long)

    ​ 解决方案:

    • 手写高精度 但是这涉及到高精度取余,不好写
    • 两个(long ; long)手动模拟(128)位整数 这个就是位运算版的高精了
    • __int128 这个很方便,只是需要自己手写读入输出优化
    #include <cstdio>
    #include <algorithm>
    #include <cctype>
    using namespace std;
    typedef __int128 type;//我也是迫不得已
    const type INF = 0x7fffffffffffffff;//就用long long的极值吧
    template <typename T>
    inline void read(T &x){//读入优化,注意有负数
        x = 0;char c = getchar();int f = 1;
        while(!isdigit(c))f = (c == '-') ? (-1) : f,c = getchar();
        while(isdigit(c))x = x * 10 + c - '0',c = getchar();
        x *= f;
    }
    char buf[32];
    template <typename T>
    inline void write(T x){//输出优化
        int p = 0;
        do{
            buf[++p] = x % 10;
            x /= 10;
        }while(x);
        for(;p;p--)
            putchar(buf[p] + '0');
    }
    type temp_d,d,temp_f,f,x,mod,ans = -INF;//几个临时变量,具体后面回收
    int n;
    int main(){
    #ifdef LOCAL
        freopen("fafa.in","r",stdin);//强烈建议使用宏编译,本机调试超级方便
    #endif
        read(n),read(mod);
        read(x);//第一个小朋友很特殊
        temp_d = d = x;//temp_d表示以i结尾的最大连续子段和,d表示区间[1,i]内的最大连续子段和
        f = d;//由题意可知第一个小朋友的分数
        temp_f = f + d;//temp_f表示区间[1,i - 1]内最大的f[j] + d[j]
        ans = max(ans,f);//更新答案
        for(int i = 2;i <= n;i++){
            read(x);//读入
            f = temp_f;//进行f的转移
            ans = max(ans,temp_f);//更新答案
            temp_d = max(temp_d + x,x);//求以i结尾的最大连续子段和
            d = max(d,temp_d)//更新[1,i]内的最大连续子段和
            temp_f = max(temp_f,f + d);//更新临时变量
        }
        if(ans < 0)putchar('-'),ans = -ans;//特判负数
        write(ans % mod);putchar('
    ');//输出
        return 0;
    }
    

    这份代码运行时间还可以进一步缩短,(fread)读入优化蒟蒻懒得写了,各位有兴趣可以冲一下最优解

  • 相关阅读:
    exit()和_exit()函数(转)
    C语言struct内存占用问题 (转)
    C语言中memset函数(转)
    STDIN_FILENO与stdin区别(转)
    stderr,strerror,errno,perror,论坛大神的回答!
    C++ 函数模版
    C++ 内置函数
    offsetof
    LockSupportDemo
    读写锁,await和signa
  • 原文地址:https://www.cnblogs.com/colazcy/p/11514760.html
Copyright © 2011-2022 走看看