题目描述
2022/10/10 每日一题
输入:给定两个长度相等且不为空的整型数组 nums1
和 nums2
,在一次操作中可以交换 nums1[i]
和 nums2[i]
的元素。
输出:使 nums1
和 nums2
严格递增所需操作的最小次数 。
用例保证可以实现操作。
算法思路
算法:求最小次数可以理解为求全局最优解,并且仅使用当前信息分析是否对当前位置进行操作容易陷入局部最优解,因此考虑动态规划来寻找最优解。
1.首先保证用例可以实现操作,则可知给定序列的每个i位置元素一定至少满足下列条件之一:
条件一:nums1[i] > nums1[i - 1] && nums2[i] > nums2[i - 1]
条件二:nums1[i] > nums2[i - 1] && nums2[i] > nums1[i - 1]
2.然后依次分析三种情况:
仅满足条件一时:
若i位置不交换,则i-1位置也必不能交换,否则导致序列不严格递增;
若i位置交换,则i-1位置也必须交换,否则导致序列不严格递增。
同理可得,仅满足条件二时:
若i位置不交换,则i-1位置也必须交换,否则导致序列不严格递增;
若i位置交换,则i-1位置也必不能交换,否则导致序列不严格递增。
满足条件一、二均满足时,交换与否都不影响序列严格递增。
3.动态规划的核心思想就是模拟每一种可能情况,并使用数组存储每一步情况的结果。本题初拟使用一个num.length * 2
大小的数组
[i][0]
表示第i位置不交换时i之前序列满足严格递增所需最小操作数
[i][1]
表示第i位置交换时i之前序列满足严格递增所需最小操作数
4.每个位置状态求解只需前一个状态的解信息,因此可使用滚动数组以减小空间复杂度。
5.观察条件求解公式发现条件可合并
以a为例,a的值只有三种情况:at,bt+1,前两者中的最小值,两种答案:at,bt+1
此时可以设置两个min函数和一个最大值num.length
进行区分:两个条件总有一个满足,第一次比较后a被置为at或者bt+1
再判断若第二种情况仍满足,则将当前已更新的a与另一个数进行比较得到最小值
代码实现
过程为维护一个2x2数组(也可使用四个变量),at和bt表示i-1的状态,a和b表示当前待求解状态
at, a
bt, b
初始化时将i = 0处的状态设为0,1
每轮循环前先将前一轮循环的解前移到at,bt中,并将当前待求状态置为最大交换次数num.length
以便后续一定被置为at或者bt+1。
public int minSwap(int[] nums1, int[] nums2) {
int n = nums2.length;
int a = 0, b = 1;
//从i = 1处开始求解
for(int i = 1; i < n; i++){
int at = a, bt = b;
a = b = n;
if(nums1[i] > nums1[i - 1] && nums2[i] > nums2[i - 1]){
a = Math.min(at, a);
b = Math.min(bt + 1, b);
}
if(nums1[i] > nums2[i - 1] && nums2[i] > nums1[i - 1]){
a = Math.min(bt, a);
b = Math.min(at + 1, b);
}
}
return Math.min(a, b);
}