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、B,两个数组长度相同,并且题目限定了能够交换的只能是相同的位置。我们需要判断的是使用最少交换次数使两个数组严格递增中的那个次数。实质上,我们要知道的是,该数组的每一位(A[i] 或者 B[i] 因为交换是针对彼此的)是否需要交换,同时,我们还要确保这一位最终是否交换保证了最少交换的次数。
能够立刻想到的一个方法是穷举,即把每一位都交换,然后挑出符合条件的且次数最少的,然而回溯我并没有掌握,并且我认为这个方法效率不高, 我们应该利用好题目给出的限制条件,即仅交换相同位置。
我们对每个位置 i 作这样的思考:i 是换还是不换?
例如对于样例,我们思考0位置换不换,接着思考1位置换不换…最后思考3位置换不换。对于每个位置,都有这么两种策略,那么如何抉择?
由于题目的要求是,返回最少的交换次数,那么抉择的依据就很简单了,就是哪种决定能保证最少的交换次数,就采用哪种决定。我们假设用两个变量 keep、swap 表示当前位置做下相应决策之后所付出的代价(相应决策即keep表示不换,swap表示换,代价表示交换次数)。

使用样例进行演绎:
① 对于位置0,实际上这是一个初始化工作,因为只有一个数,换不换皆为严格递增状态,那么可知此时:keep = 0, swap = 1. (不换,次数自然是0,换了一次,次数自然是1);
② 对于位置1,如果不换,并不会对位置01构成的数组的状态有影响,为严格递增;如果交换,有一点值得注意,此时需要分类讨论:是否只换这一个位置? 如果就针对样例的情况,我们可以大声的回答是;
然而这样的情况呢?A = [3, 6] B = [1, 2]
此时,如果我们执意要交换第1个位置,可以看到对于第1个位置的6与2来说,仅仅交换2与6,A就变成3, 2,依旧不符合严格递增的情况,这个时候就必须考虑上之前交换过的3与1,让A变成1, 2,而B成为3, 6才能达成条件。实际上若对于样例中的情况,这么做并不会出现问题。
然而,我们并不应该忽视这样的情况。因为事实上,对于样例的情况,这种情况(即只换这一个位置可以达成条件)已经出现了。而这种情况的条件也很明了,即A的当前位置上的数可以满足B的情况,而B也可以满足A的情况(即 A[i-1]< B[i] && B[i-1]< A[i])。

通过以上的探索,我们可以得到以下结论:
① 每一个位置的交换与否仅与前一个位置有关系,而这前一个位置不仅包括当前数组,还包括另一数组;
② 当前位置若满足递增状态(即A[i-1]< A[i] && B[i-1]< B[i])时,我们考虑换或者不换。此时keep值保持为原keep值,为了确保仅在这一条件下更换是有效的,swap值需要在原swap值上增1,即前面的交换保留。
③ 若当前位置满足A[i-1]< B[i] && B[i-1]< A[i] 这时意味着仅交换这一位置也能保证整体的严格递增状态。此时数组若不满足严格递增(即不满足②),keep的值变化为原swap值,因为要保证当前位置不交换,只有交换其他位置才能保证递增,而swap的值变为原keep值加1,原因前面分析过。

以上说法用keep值和swap值的说法有些别扭,不妨令其为数组,其中keep[i]表示为“为保持第i个位置不交换所付出的代价”,而swap[i]则表示 “使第i个位置交换所付出的代价“。其实将其设为数组,这一做法很动态规划:)

具体代码:

class Solution {
    public int minSwap(int[] A, int[] B) {
        int [] keep = new int [A.length];
        int [] swap = new int [A.length];
        keep[0] = 0;
        swap[0] = 1;
        for(int i=1; i<A.length; i++){
            keep[i] = swap[i] = Integer.MAX_VALUE;
            if(A[i-1]<A[i]&&B[i-1]<B[i]){
                keep[i] = keep[i-1];
                swap[i] = swap[i-1]+1;
            }
            if(A[i-1]<B[i]&&B[i-1]<A[i]){	//此处条件并不与之前递增条件矛盾,故需要同时判断
                keep[i] = Math.min(keep[i],swap[i-1]);
                swap[i] = keep[i-1]+1;
            }
        }
        return Math.min(keep[A.length-1],swap[A.length-1]);
    }
}

吐槽:

官方题解实在是看不懂。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值