「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<iostream>
#include<algorithm>
#include<cstring>

#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<<ans<<endl;
        
    }
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

green qwq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值