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
提示:
-
S.length <= 1000
-
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;
}
};