周赛2167. 移除所有载有违禁货物车厢所需的最少时间

题目

给你一个下标从 0 开始的二进制字符串 s ,表示一个列车车厢序列。s[i] = ‘0’ 表示第 i 节车厢 不 含违禁货物,而 s[i] = ‘1’ 表示第 i 节车厢含违禁货物。

作为列车长,你需要清理掉所有载有违禁货物的车厢。你可以不限次数执行下述三种操作中的任意一个:

从列车 左 端移除一节车厢(即移除 s[0]),用去 1 单位时间。
从列车 右 端移除一节车厢(即移除 s[s.length - 1]),用去 1 单位时间。
从列车车厢序列的 任意位置 移除一节车厢,用去 2 单位时间。

返回移除所有载有违禁货物车厢所需要的 最少 单位时间数。
注意,空的列车车厢序列视为没有车厢含违禁货物。

提示:

1 <= s.length <= 2 * 105
s[i] 为 '0' 或 '1'

示例 :

输入:s = "1100101"
输出:5
解释:
一种从序列中移除所有载有违禁货物的车厢的方法是:
- 从左端移除一节车厢 2 次。所用时间是 2 * 1 = 2 。
- 从右端移除一节车厢 1 次。所用时间是 1 。
- 移除序列中间位置载有违禁货物的车厢。所用时间是 2 。
总时间是 2 + 1 + 2 = 5 。

一种替代方法是:
- 从左端移除一节车厢 2 次。所用时间是 2 * 1 = 2 。
- 从右端移除一节车厢 3 次。所用时间是 3 * 1 = 3 。
总时间也是 2 + 3 = 5 。

5 是移除所有载有违禁货物的车厢所需要的最少单位时间数。
没有其他方法能够用更少的时间移除这些车厢。

思路

对于车厢i,可能从左边移除时间单位少,也可能从右边移除时间单位小。
因此如何判断出当前车厢的移除方式是解题的关键,也是最困扰我的地方。
这里应注意的是,前i个车厢可能用左移除,i+1到len个车厢可能右移除,并不是只能用一种方式移除。

因为左右都可以移除车厢,我们可以分别从左边和右边都进行车厢的移除。最后对于每一节车厢,判断通过哪种方式花费时间最少。
这里可以采用前缀和的方式记录到i节车厢要花费的时间,后面方便判断两种时间大小。
前缀和采用数组ans进行存储。

但是怎么进行记录呢,我们以从右边移除的方式为例:

  • 当s[i]==‘0’,不需要移动该车厢,花费时间和上一个车厢移除时间相同,因此ans[i]=ans[i+1]
  • 当s[i]==‘1’,需要移动该车厢,此时需判断移动它自己(花费两个单位时间)还是将它和它前面全部移走划算。即ans[i] = min(ans[i+1]+2, len-i)。(ans[i+1]+2为移走自己花费2个单位时间,len-i为移动它前面所有,因为是从右往左的移动,相当于移动它和它右边的车厢)

这里移动它前面所有车厢可能难以理解,在比赛时我也没想到。
其实很简单,比如"00001101",从右到左:

  1. 第一个为1,因为在最右端,花费1个单位时间,它右面没有车厢,相当于移动它前面所有车厢
  2. 第二个为2,不移动
  3. 第三个为1,此时它可以移除自己,总共花费(1+2)个单位时间;同时可以移动它和它右面所有车厢,花费3个单位时间
  4. 第四个为1,此时它可以移除自己,花费(1+2+2)个单位时间;可以移动它和它右面所有车厢,花费4个单位时间。此时应选择移动右边所有车厢。

因此实际上,对于每一节车厢,我们只需要考虑两种情况。

从右向左的前缀和数组存好后,此时ans[i]就是从i-len车厢从右向左移除的最小所需时间单位。

接着我们可以从左向右移除进行计算,采用从左向右前缀和:
对于当前车厢i,像前面一样判断0-i车厢移除违禁货物所需最小单位时间
假设left记录前i个车厢最小单位时间,那么left+ans[i+1]就代表前i个车厢从左移除,后i+1到len个车厢代表从右移除。
枚举一遍所有情况,就能找到最小值

代码

class Solution {
public:
    int minimumTime(string s) {
        int len = s.size(), minn = 0;
        vector<int> ans(len+1);
        for(int i=len-1;i>=0;i--)
            ans[i] = s[i]=='0'? ans[i+1]:min(ans[i+1]+2, len-i);
        minn = ans[0];
        int left = 0;
        for(int i=0;i<len;i++){
            if(s[i]=='1')
                left = min(left+2, i+1);
            minn = min(minn, left+ans[i+1]);
        }
        return minn;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值