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

在这里插入图片描述
解题思路:使用两个状态量来存储信息:
swap[i]表示交换A和B中第i个数字,A和B中前i个数字达到严格递增所需要的最小交换次数。
keep[i]表示不交换A和B中第i个数字,A和B中前i个数字达到严格递增所需要的最小交换次数。

对于A和B中的第i个数字,要考虑两种情况。
第一种情况是A[i]>A[i-1] && B[i]>B[i-1],同时有A[i]>B[i-1] && B[i]>A[i-1],这时候对于swap[i]和keep[i]的状态转移方程如下:

if(a[i]>a[i-1] && b[i]>b[i-1])
{
    if(a[i]>b[i-1] && b[i]>a[i-1])
    {
        swap[i] = min(swap[i-1],keep[i-1]) + 1;
        keep[i] = swap[i] - 1;
    }
}

在符合上面两个条件的情况下,如果要交换A和B中的第i个数字,则可以找min(swap[i-1],keep[i-1])+1,因为不管是交换第i-1个数字还是保持第i-1数字,对于是否交换第i个数字都没有影响,因为都符合条件,因此我们选择swap[i-1]和keep[i-1]中较小的那个值,然后加1。这样就得到了在符合条件下的交换第i个数字所需要的最小交换次数。

同理,对于keep[i],因为不管是交换第i-1个数字得到的swap[i-1]还是不交换第i-1个数字得到的keep[i-1],都对keep[i]没啥影响,因此我们选择swap[i-1]和keep[i-1]中较小的那个,并赋值给keep[i]。也可以表示为keep[i] = swap[i] - 1。具体的形式就和上面的代码一下。

要是符合条件A[i]>A[i-1] && B[i]>B[i-1],但是不符合条件A[i]>B[i-1] && B[i]>A[i-1]时,我们需要考虑另外的情况,具体的C++代码如下:

if(a[i]>a[i-1] && b[i]>b[i-1])
{
    if(a[i]>b[i-1] && b[i]>a[i-1])  //两种情况都符合
    {
         swap[i] = min(swap[i-1],keep[i-1]) + 1;  
         keep[i] = swap[i] - 1;
    }
    else //不符合a[i]>b[i-1] && b[i]>a[i-1]
    {
         swap[i] = swap[i-1] + 1;
         keep[i] = keep[i-1];
    }
}

在符合a[i]>a[i-1] && b[i]>b[i-1]条件,但是不符合a[i]>b[i-1] && b[i]>a[i-1]条件时,要找swap[i]的状态转移方程,可以分析一下,因为不符合a[i]>b[i-1] && b[i]>a[i-1],如果我们交换了第i个数字,那么前i个数字必定不符合严格递增关系。如果要变成符合严格递增关系的子集,则要将前i-1个数组进行变换。很幸运,swap[i-1]表示的是交换第i-1个数字得到严格递增关系的数组所需的最小交换次数。那么就可以得到swap[i] = swap[i-1] + 1。

同理,对于keep[i],因为第i个数字不符合a[i]>b[i-1] && b[i]>a[i-1],要求第i个数字保持不变得到前i个数组严格递增所需的最小交换次数,则分析前i-1个数组中的swap[i-1]和keep[i-1]。可以得知keep[i] = keep[i-1] + 1。意思是要求第i个数字不变的最小交换次数,则可以在第i-1个数字不变得到递增数组所需的最小交换次数上加1。注意不能用到swap[i-1],因为swap[i-1]是交换了第i-1个数字的。当交换第i-1个数字时,如果第i个数字不交换,是没办法得到严格递增数组的。

第二种情况是A[i]<A[i-1] || B[i]<B[i-1],这种情况下考虑swap[i]和keep[i]的状态转移方程,根据上面的分析,我们可以简单地得到:
swap[i] = keep[i-1]+1;
keep[i] = swap[i-1];
这两个状态转移方程的意思是,如果交换第i个数字,则是在第i-1个数字不交换得到递增数组的最小交换次数的程度上+1。同理,如果不交换第i个数字,则因为违反了严格递增关系,需要把前i-1个数组进行变换,那么恰好swap[i-1]的意思就是交换第i-1个数字得到严格递增数组所需的最小交换次数,那么就可以得到keep[i] = swap[i-1];

综上分析,可以得到整体C++代码如下:

class Solution {
public:
    int minSwap(vector<int>& a, vector<int>& b) {
        int length = a.size();
        vector<int> swap(length,1);  //用于存储交换第i个数字,使前i个数字达到严格递增数组所需的最小交换次数
        vector<int> keep(length,0);  //用于存储不交换第i个数字,使前i个数字达到严格递增数组所需的最小交换次数
        for(int i=1;i<length;i++)
        {
            if(a[i]>a[i-1] && b[i]>b[i-1])  //如果A(B)中第i个数字和A(B)中第i-1个数字符合严格递增关系
            {
                if(a[i]>b[i-1] && b[i]>a[i-1])  //如果A(B)中第i个数字和B(A)中第i-1个数字符合严格递增关系
                { 
                    swap[i] = min(swap[i-1],keep[i-1]) + 1;
                    keep[i] = swap[i] - 1;
                }
                else  //如果A(B)中第i个数字和B(A)中第i-1个数字不符合严格递增关系
                {
                    swap[i] = swap[i-1] + 1;
                    keep[i] = keep[i-1];
                }
            }
            else  //如果A(B)中第i个数字和A(B)中第i-1个数字不符合严格递增关系
            {
                swap[i] = keep[i-1] + 1;
                keep[i] = swap[i-1];
            }
        }
        return min(swap[length-1],keep[length-1]);  //返回交换或者不交换两者之间的较小的值
    }
};

这里解题的关键是要理解swap[i]和keep[i]代表的意义,并理解swap[i-1]、swap[i]、keep[i-1]、keep[i]之间的关系。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值