leetcode_1: 最少移动次数问题

453:最小移动次数使数组元素相等

题目描述:
给定一个长度为 n 的非空整数数组,找到让数组所有元素相等的最小移动次数。每次移动可以使 n - 1 个元素增加 1。

示例:
输入:
[1,2,3]
输出:
3
解释:
只需要3次移动(注意每次移动只会增加两个元素的值):
[1,2,3] => [2,3,3] => [3,4,3] => [4,4,4]

思路:
n-1个元素同时加1等价于某个元素减1

代码:

class Solution {
public:
    int minMoves(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        int length = nums.size();
        int sum = 0;
        for(int i=1;i<length;i++)
        {
            sum += nums[i] - nums[0];
        }
        return sum;
    }
};

462. 最少移动次数使数组元素相等 II

题目描述:
给定一个非空整数数组,找到使所有数组元素相等所需的最小移动数,其中每次移动可将选定的一个元素加1或减1。 您可以假设数组的长度最多为10000。

示例:
输入:
[1,2,3]
输出:
2
解释:
只有两个动作是必要的(记得每一步仅可使其中一个元素加1或减1):
[1,2,3] => [2,2,3] => [2,2,2]

思路:
中位数是最优解。
证明:假设有2n+1个数,升序排序为…a, m, b…,其中m是中位数,那么m左边有n个数,右边也有n个数。我们假设将左边的数变成m的代价为x,右边的数变成m的代价为y,此时的总代价是t=x+y。下面我们尝试把所有的数都转变成a,则a右面的数转变成a的代价为y+(m-a)*(n+1), a左边的数转变成a的代码为x-(m-a)*n,则总代价为t=x+y+m-a,同理,如果把所有的数都转变成b,则总代价为t=x+y+b-m。由此可见,当总数为2n+1时,选择中位数是最优解。
假设有2n个数,升序排序为…a, b…,这样a左边有n-1个数,b右边有n+1个数。假设a左边数转换成a的代价为x,b右边的数转换为a的代价为y,总代价为t=x+y+b-a。尝试将所有数都转换为b,则总代价为t=x + (b-a)n + y - (b-a)(n-1) = x + y + b- a。对于总数为偶数的情况,选择两个中位数的移动步数都是一样的。

代码:

class Solution {
public:
    int minMoves2(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        int length = nums.size();
        int mid = nums[length/2];
        int sum = 0;
        for(int i=0;i<length;i++)
        {
            sum += (nums[i] > mid) ? (nums[i] - mid) : (mid - nums[i]);
        }
        return sum;
    }
};

注:可用二分法查找中位数,复杂度更低

921. 使括号有效的最少添加

题目描述:
给定一个由 ‘(’ 和 ‘)’ 括号组成的字符串 S,我们需要添加最少的括号( ‘(’ 或是 ‘)’,可以在任何位置),以使得到的括号字符串有效。

从形式上讲,只有满足下面几点之一,括号字符串才是有效的:

  • 它是一个空字符串,或者

  • 它可以被写成 AB (A 与 B 连接), 其中 A 和 B 都是有效字符串,或者

  • 它可以被写作 (A),其中 A 是有效字符串。

给定一个括号字符串,返回为使结果字符串有效而必须添加的最少括号数。

示例1:
输入:"())"
输出:1
示例 2:
输入:"((("
输出:3
示例 3:
输入:"()"
输出:0
示例 4:
输入:"()))(("
输出:4

提示:

  1. S.length <= 1000

  2. S 只包含 ‘(’ 和 ‘)’ 字符。

思路:
遍历字符串,设置两个变量,left表示’(‘的数目,right表示’)‘的数目。当遇见’(‘的时,left++;当遇见’)‘时,如果left>0,就说明前面有与’)‘匹配的’(’,则需要将left–;如果left<0,说明’)‘不可匹配,right++。最后返回left+right,即是不能匹配的’(‘和’)'的总数目。

代码:

class Solution {
public:
    int minAddToMakeValid(string S) {
        int length = S.length();
        int left = 0;
        int right = 0;
        for(int i=0;i<length;i++){
            if(S[i] == '('){
                left++;
            }else if(S[i] == ')'){
                if(left){
                    left--;
                }else{
                    right++;
                }
            }
        }
        return left + right;
    }
};

801. 使序列递增的最小交换次数

题目描述:
我们有两个长度相等且不为空的整型数组 A 和 B 。我们可以交换 A[i] 和 B[i] 的元素。注意这两个元素在各自的序列中应该处于相同的位置。在交换过一些元素之后,数组 A 和 B 都应该是严格递增的(数组严格递增的条件仅为A[0] < A[1] < A[2] < … < A[A.length - 1])。

给定数组 A 和 B ,请返回使得两个数组均保持严格递增状态的最小交换次数。假设给定的输入总是有效的。

示例:
输入: A = [1,3,5,4], B = [1,2,3,7]
输出: 1
解释:
交换 A[3] 和 B[3] 后,两个数组如下:
A = [1, 3, 5, 7] , B = [1, 2, 3, 4]
两个数组均为严格递增的。

注意:

  • A, B 两个数组的长度总是相等的,且长度的范围为 [1, 1000]。

  • A[i], B[i] 均为 [0, 2000]区间内的整数。

思路:
根据官方题解才看明白具体是怎么回事。
本题使用动态规划思想,判定A[i]和B[i]是否交换时,需要考虑它们之前的元素A[i-1]和B[i-1]进行被交换。那么我们先定义:n1 表示数组A和B满足前i - 1个元素分别严格递增,并且 A[i - 1] 和 B[i - 1] 未被交换的最小交换次数,s1 表示 A[i - 1] 和 B[i - 1] 被交换的最小交换次数。用n2和s2分别表示前i个元素严格递增时,A[i]和B[i]未被交换和被交换的最小次数。

下面考虑两种情况:

  • 如果A[i-1]< A[i],并且B[i-1]< B[i],如果A[i-1]和B[i-1]未交换,那么A[i]和B[i]就不需要交换,故n2=min(n2,n1);如果A[i-1]和B[i-1]被交换,那么A[i]和B[i]也要被交换,故s2=min(s2, s1+1)。

  • 如果A[i-1] < B[i],并且B[i-1] < A[i],那么A[i-1]和B[i-1],A[i]和B[i]必须被交换一对才能满足严格递增,故n2 = min(n2, s2)和s2 = min(s2, n1 + 1)

上面两种情况可能会同时发生,不过我自己考虑,会把第一种情况忽略掉,第二种相对比较直观好理解些。

代码:

class Solution {
public:
    int minSwap(vector<int>& A, vector<int>& B) {
        int length = A.size();
        int n1 = 0;
        int s1 = 1;
        for(int i=1;i<length;i++){
            int n2=2001;
            int s2=2001;
            if(A[i-1]<A[i] && B[i-1]<B[i]){
                n2 = Min_num(n2, n1);
                s2 = Min_num(s2, s1 + 1);
            }
            if(A[i-1] < B[i] && B[i-1] < A[i])
            {
                n2 = Min_num(n2, s1);
                s2 = Min_num(s2, n1 + 1);
            }
            n1 = n2;
            s1 = s2;
        }
        return Min_num(n1,s1);
    }
    int Min_num(int a, int b){
        return a<b?a:b;
    }
};
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

马鹤宁

谢谢

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

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

打赏作者

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

抵扣说明:

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

余额充值