• 「Daily OI Round 4」Snow(贪心+模拟)


    「Daily OI Round 4」Snow

    题目描述

    下雪了,小 Y 堆了 n n n 个雪柱排成一排,调皮的小 X 打算推倒这 n n n 个雪柱,他想在最少的时间内推倒所有雪柱,于是他请你来为他出谋划策。

    小 Y 堆的每个雪柱高 a i a_i ai 单位,推倒一个雪柱的时间为该雪柱的高度。小 X 只能从这一排雪柱的两端推雪柱 *,次数不限,小 X 移动的时间可以忽略不计。这样就可以使一个雪柱倒向其他雪柱,从而击倒另一个雪柱(击倒的时间忽略不计),然后发生连锁反应,更加节省时间 **。

    设初始的势能为当前手动推倒的雪柱 k k k 的高度 p = a k p=a_k p=ak,则此轮连锁反应中第 i i i 个雪柱倒向第 j j j 个雪柱时:

    • p ≥ a j p\ge a_j paj,则使第 j j j 个雪柱也被击倒,并令 p ← a j p \gets a_j paj
    • p < a j p< a_j p<aj,则第 j j j 个雪柱的高度减少(不被击倒),终止整个连锁反应,令 a j ← a j − p a_j \gets a_j-p ajajp

    请你求出推倒所有雪柱的最短时间。

    *:每一次要么从左边推最左边的雪柱,要么从右边推最右边的雪柱。

    **:雪柱的倒塌方向取决于推雪柱的方向,如果从左边推,雪柱就会向右依次倒塌(第 i i i 个雪柱倒塌向第 i + 1 i+1 i+1 个雪柱),反之同理。

    输入格式

    本题有多组测试数据。

    第一行一个整数 T T T,表示数据组数。

    对于每组数据:

    第一行一个整数 n n n,表示雪柱的数量。

    第二行 n n n 个整数,分别表示每个雪柱的高度。

    输出格式

    对于每组数据:输出一行一个整数,表示推倒所有雪柱的最短时间。

    样例 #1

    样例输入 #1

    3
    5
    2 3 1 4 5
    6 
    6 6 6 6 6 6
    6
    1 1 4 5 1 4
    

    样例输出 #1

    7
    6
    8
    

    提示

    【样例解释】
    • 对于第一组数据:

    第一次从左边推,耗费 2 2 2 点时间,使得 5 5 5 个雪柱 的高度分别变为: 0 , 1 , 1 , 4 , 5 0,1,1,4,5 0,1,1,4,5

    第二次从右边推,耗费 5 5 5 点时间,使得所有雪柱都被击倒。

    共耗费 7 7 7 点时间。

    • 对于第二组数据:

    从左边或者右边都可以一次性推完,共耗费 6 6 6 点时间。

    • 对于第三组数据:

    第一次从右边推,耗费 4 4 4 点时间,使得 6 6 6 个雪柱的高度分别变为: 1 , 1 , 4 , 4 , 0 , 0 1,1,4,4,0,0 1,1,4,4,0,0

    第二次从右边推,耗费 4 4 4 点时间,使得所有雪柱都被击倒。

    共耗费 8 8 8 点时间。

    【数据范围】

    本题采用捆绑测试。

    Subtask \text{Subtask} Subtask分值 n ≤ n \le n
    0 0 0 10 10 10 20 20 20
    1 1 1 15 15 15 100 100 100
    2 2 2 25 25 25 1000 1000 1000
    3 3 3 50 50 50 1 0 5 10^5 105

    对于全部数据,保证: 1 ≤ T ≤ 10 1 \le T \le 10 1T10 1 ≤ n ≤ 1 0 5 1 \le n \le 10^5 1n105 1 ≤ a i ≤ 1 0 10 1 \le a_i \le 10^{10} 1ai1010

    思路

    我们可以暴力的枚举一下往左推和往右推得到的结果。最终综合一下即可。

    那具体怎么写呢?

    • 首先,我们可以用 d1,l1,d2,l2来分别装往左推的价值、当前左端的雪的高度(为什么这样呢?因为题目要求只能在左或右去推)、右推的价值、当前右端的雪的高度。
    • 之后开始枚举的时候,我们要分 3 种情况更新:1.当上一个左端点为0,此时更新左端点的值,此时就把左边的推了;2.当左端点的高度大于当前点,那么我们直接推了(就连锁反应);3.当左端点的高度小于当前点,说明你推了无法发生连锁反应。
    • 然后右边也同理。
    • 那最后的答案怎么求呢?题目说要求最小的,那么我们就依次枚举以哪个点为核心,然后推到它前面和后面的时间加起来就是我们的答案,然后我们要取 m i n min min
    • 细节1:开long long。
    • 细节2:要memset一下。

    代码

    //直接模拟
    #include
    #include
    #include
    
    #define int long long
    
    using namespace std;
    
    const int N = 1e5+10;
    
    int w[N];
    int d1[N],l1[N];
    int d2[N],l2[N];
    int n,T;
    int ans;
    
    signed main(){
        cin>>T;
        
        while(T--){
            
            memset(d1,0,sizeof d1);
            memset(d2,0,sizeof d2);
            memset(l1,0,sizeof l1);
            memset(l2,0,sizeof l2);
            ans=1e18;
            cin>>n;
            
            for(int i=1;i<=n;i++)cin>>w[i];
            
            //往左推
            for(int i=1;i<=n;i++){
                d1[i]=d1[i-1];
                if(l1[i-1]==0)d1[i]+=w[i],l1[i]=w[i];//若前一个雪柱被完全推倒,则当前雪柱独立推倒
                else if(l1[i-1]>=w[i])l1[i]=w[i];//若前一个雪柱未倒下且足够的势能推倒当前雪柱
                else{
                    l1[i]=w[i]-l1[i-1],d1[i]+=(w[i]-l1[i-1]);// 若前一个雪柱部分倒下,计算当前雪柱被推倒的势能
                }
            }
            //从右推
            for(int i=n;i>=1;i--){
                d2[i]=d2[i+1];
                if(l2[i+1]==0)d2[i]+=w[i],l2[i]=w[i];
                else if(l2[i+1]>=w[i])l2[i]=w[i];
                else{
                    l2[i]=w[i]-l2[i+1],d2[i]+=(w[i]-l2[i+1]);
                }
            }
            
            for(int i=1;i<=n;i++)ans=min(ans,d1[i-1]+d2[i+1]+max(0ll,w[i]-(l1[i-1]+l2[i+1])));
            
            
            cout<
  • 相关阅读:
    【taro react】---- 兼容微信小程序和H5的海报绘制插件
    leetcode 167. 两数之和 II - 输入有序数组
    JavaScript 语法基础
    英语语音篇 - 自然拼读
    理解系统内核linux phy驱动
    一次带你掌握MVC和MVVM的区别
    常用端口与udp协议
    Unity设置TextMeshPro文本超出范围显示...
    vue学习笔记,购物车清单制作
    nginx常用命令
  • 原文地址:https://blog.csdn.net/m0_60738889/article/details/139424724