zoukankan      html  css  js  c++  java
  • luogu P3078/P5019/P1969

    三倍经验

    NOIP2018 铺设道路

    NOIP2013 积木大赛

    题意

    给出一个长度为n的数列A,每次可以任选一段区间[l,r],使A[l]到A[r]全部-1,最终将整个序列减到0,求最小操作次数.

    分析

    一开始的序列可以具象成这个样子
    一开始的序列可以转化成这个样子.
    每一次选取一段区间-1,可以怎么操作呢?

    1.暴力枚举区间,然后暴力-1

    emmm,时间复杂度:O(不能过),pass.
    ※期望得分:0;

    2.线段树

    emmmmmmm,勇气可嘉,但是还要动动脑子,不要一看到区间加减就开始上奇怪的数据结构.

    3.O(n)递推

    其实这是我在做NOIP2013/2018的T1时的做法.看图:

    划分区间
    很容易可以看出,两个红色框部分是不能一起减掉的,只能两堆分开减,因为中间有一个低谷将两边的堆隔开,那么在最优情况下,这个低谷一定比两堆更早减为0,于是两堆注定隔开.但是下面蓝色框的部分就属于两堆的公共部分,只要在减其中一堆时顺便减掉它就好了.而且,加入我们把序列的第n+1位看成0的话,它就相当于一个一低到底的低谷,有了这些,我们就可以得出以下思路:

    划分区间

    如图,每当我们遇到一个低谷,我们就将目前路过的这座山峰,也就是上一个峰顶-这一个谷底的高度减去,这样最后当遇到第n+1位的0时,一定能将剩下所有的高度减掉.这样就保证了只有被迫隔开的山峰是需要分别减去的,而公共部分的次数都没有浪费.不明白的话可以画图模拟一下.

    核心代码:

    long long h[N], ans//A序列,答案;
    
    for (int i = 1; i <= n ; ++i) {
        if (h[i] > h[i + 1]) {//当遇到峰顶时记录下现在的高度
    	long long temp = h[i];
    	while (h[i] > h[i + 1]) //一路找到谷底
                i++;
    	ans += temp - h[i];记录答案
        }
    }
    

    这样就可以解决了!

    ※期望得分:100

    4.差分

    差分是一个绝妙的思想,对A建立差分数组B,差分数组意味着B[i]=a[i]-a[i-1],也就是将A序列每一位和前一位的差记录下来.

    如果要实现区间[l,r]-1,我们只需要B[l]-1,B[r+1]+1就可以了(想一想,为什么)

    于是这个A序列就变成了B序列.要想让A序列归零,我们就要让B序列归零(想一想,是不是).显然,B中的每一个正数,后面都有负数与它对应,因此我们只需要将这个序列中所有的正数加起来,就是我们操作的总次数!

    核心代码:

    long long A[N], B[N], ans;
    
    int main() {
        cin >> n;
        for (int i = 1; i <= n; ++i) {
            cin >> A[i];
            B[i] = A[i] - A[i - 1];
            if (B[i] > 0)
                ans += B[i];
        }
        cout << ans << endl;
        return 0;
    }
    

    ※期望得分:100

    最后还有一句话要提醒:

    三年OI一场空,不开long long见祖宗

    欢迎批评和指正!

  • 相关阅读:
    接口开发中的 RestTemplate 传参问题
    逆流成河:五年软件开发生涯
    .NET Web开发技术简单整理
    2011-05-29 21:48 VS.NET2010水晶报表安装部署[VS2010]
    WPF 基础到企业应用系列3——WPF开发漫谈
    C# WinForm开发系列
    接口和委托的区别
    通过jquery触发select自身的change事件
    php去掉字符串中的最后一个字符和汉字
    Go语言学习之数据类型
  • 原文地址:https://www.cnblogs.com/i-cookie/p/11383749.html
Copyright © 2011-2022 走看看