• CodeForces


    题目链接

    题目大意

      给n个数,你可以使每个数的值减小(最小减到1),问把这n个数改成没有左右两个数都大于中间数的情况最少需要减去多少。

    解题思路

      先考虑暴力解法,枚举每个数作为最大的那个数,然后分别从左右两个方向开始,如果当前的数字大于之前的数字,就将他改成之前的那个数。
      然后我们可以考虑dp来做,(dp[i])表示以i为最大值的时候需要减去的最少的数(左右都要进行一次,这里只讲左边),如果这一列数使非严格单调递增的话,代价自然是0,否则的话,如果当前位置i的数小于之前的数,因为对于前面的所有方案,都是非严格单调递增的,所以只要找到一个前面一个位置j,满足位置j上的数小于当前的数,然后让他们之间的数都改成位置i上的数就行了。当然如果这样直接暴力查询的话必定超时,我们用单调栈来代替之前的查询,只要当前的数字比栈顶的数字小,就把栈顶弹出,新的栈顶就是比当前数字小的值了。左右都跑一遍,然后枚举中间点就行了。

    代码

    const int maxn = 5e5+10;
    int n, pos, arr[maxn]; ll pre[maxn], post[maxn];
    int main() {
        cin >> n;
        for (int i = 1; i<=n; ++i) scanf("%d", &arr[i]);
        stack<P> sk; ll sum = 0;
        for (int i = 1; i<=n; ++i) {
            int w = 1;
            while(!sk.empty() && sk.top().x>arr[i]) {
                sum -= 1LL*sk.top().x*sk.top().y;
                w += sk.top().y, sk.pop();
            }
            //cout << w << endl;
            sk.push({arr[i], w});
            sum += 1LL*arr[i]*w;
            pre[i] = sum;
        }
        while(!sk.empty()) sk.pop();
        sum = 0;
        for (int i = n; i>=1; --i) {
            int w = 1;
            while(!sk.empty() && sk.top().x>arr[i]) {
                sum -= 1LL*sk.top().x*sk.top().y;
                w += sk.top().y, sk.pop();
            }
            sk.push({arr[i], w});
            sum += 1LL*arr[i]*w;
            post[i] = sum;
        }
        //for (int i = 1; i<=n; ++i) cout << pre[i] << ' ' << post[i] << endl;
        ll ans = 0;
        for (int i = 1; i<=n; ++i) {
            ll t = pre[i]+post[i]-arr[i];
            if (t>ans) {
                ans = t;
                pos = i;
            }
        }
        int t = arr[pos];
        for (int i = pos; i>=1; --i) {
            if (t>arr[i]) t = arr[i];
            arr[i] = t;
        }
        t = arr[pos];
        for (int i = pos; i<=n; ++i) {
            if (t>arr[i]) t = arr[i];
            arr[i] = t;
        }
        for (int i = 1; i<=n; ++i) printf(i==n ? "%d
    ":"%d ", arr[i]);
        return 0;  
    }
    
  • 相关阅读:
    UI: 操作导航控制器的视图控制器数组
    UI: 使用 UIViewController 展现和管理视图
    UI: 使用 UINavigationController 实现导航
    UI: 使用 UIActivityViewController 显示分享选项
    UI: 使用 UIActivityViewController 显示分享选项
    UI: 自定义 UISegmentedControl
    UI: UISegmentedControl 做简单选项分组及其自定义
    UI: UISlider 实现指定范围值的选择
    隧道目标识别实验过程(二)标准VOC数据集和制作后的数据集实验结果对比
    隧道目标识别实验过程(一)数据集的制作
  • 原文地址:https://www.cnblogs.com/shuitiangong/p/14551768.html
Copyright © 2020-2023  润新知