转成有序数组的最少交换次数

问题描述:

第一题:现在想通过交换相邻元素的操作把一个给定序列交换成有序,最少需要交换的次数是多少?比如3 1 2 4 5需要最少交换2次。

答案:需要交换的最少次数为该序列的逆序数。

证明:可以先将最大数交换到最后,由于是相邻两个数交换,需要交换的次数为最大数后面的数的个数(可以看做是最大数的逆序数),然后,交换过后,去除最大数,再考虑当前最大数也需要其逆序数次交换。则每个数都需要交换其逆序数次操作,则总最少交换次数为序列总体的逆序数。


class Solution {
public:
int minSwap(vector&nums)
{
  if(nums.empty()||nums.size()==1)return 0;
  int res=0;
  for(int i=0;inums[j])
       {
          res+=1;
       }
    }
  }
 retrun res;
}

};

第二题:现在想通过交换任意两个元素的操作把一个给定序列交换成有序,最少需要交换的次数是多少?

答案:我认为是数字的总个数减去循环节的个数。

循环节的求法是,先将数组排序,然后根据之前的坐标和排序之后的坐标,构建成一个有向图,然后在这个图上找到环

/*
 *  交换任意两数的本质是改变了元素位置,
 *  故建立元素与其目标状态应放置位置的映射关系
 */
int getMinSwaps(vector &A)
{
    //  排序
    vector B(A);
    sort(B.begin(), B.end());
    map m;
    int len = (int)A.size();
    for (int i = 0; i < len; i++)
    {
        m[B[i]] = i;    //  建立每个元素与其应放位置的映射关系
    }

    int loops = 0;      //  循环节个数
    vector flag(len, false);
    //  找出循环节的个数
    for (int i = 0; i < len; i++)
    {
        if (!flag[i])
        {
            int j = i;
            while (!flag[j])
            {
                flag[j] = true;
                j = m[A[j]];    //  原序列中j位置的元素在有序序列中的位置
            }
            loops++;
        }
    }
    return len - loops;
}

 

第三题:给定数组 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]区间内的整数。

依照定义,确实可以通过dfs来逐个交换并且判断。但是时间复杂度是几何上升的。

一般经验是这种可以解决的问题都少不了dp。

但是dp需要一个定义,我们又怎样来定义呢?

所以这里实际上需要两个数组或者说是一组二元变量。

用swap数组中的swap[i]表示第i个元素如果要交换来使得AB成为递增序列那么需要多少次的代价。

用keep数组的keep[i]表示第i个元素不交换使得两个数组依然有序需要多少代价。

举个例子,我们再推导转移方程。

针对A=[1,3,5,7] B=[1,2,3,4]

swap[0]=1,keep[0]=0表示第0个元素为了使其保持有序,交换确实可以,但付出了1次的代价,不交换也可以,0次代价。

这只是初始化,真正的大头在后面。

swap[1]=2,keep[1]=0,这个也很显然,因为只看前两个元素的话,已经是满足条件了。那么如果我非要交换来使得顺序成立,那就需要2次,但是不交换依然可以,0次。

swap[2]=3,keep[2]=0,道理同上一段的推导。

但是swap[3]=1,keep[3]=3,这又是什么道理呢?观察AB的末尾,容易看出其实只要交换了7和4就满足了条件了,那么swap就是1。但是如果我不想交换这个元素,那么就需要把前三个都交换一次,就是3.

经过上述的推导,我们发现keep[i],swap[i]总是与keep[i-1],swap[i-1]存在着某种联系。

首先有两种情况:

一、第i个元素是满足增序的,也就是A[i]>A[i-1],B[i]>B[i-1]因为只要保持就可以了,所以keep[i]=keep[i-1],但是如果要交换,那就比swap[i-1]还多了个第i个元素的代价,也就是swap[i]=swap[i-1]+1.

二、A[i]>B[i-1] 而且B[i]>A[i-1]这种情况满足了“是不是我可以通过交换使得满足条件”,值得一提的是这两个条件应该同时判断,因为谁也不知道那种情况下会有更小的代价。但是如果交换就会是keep[i-1]+1的代价(因为实际上就是翻转了第i个再加上让前i-1个保持的代价),同理keep[i]此时等于swap[i-1]因为不翻转这个,但要翻转前i个。
代码:

//dp1[i]表示在不交换A[i]和B[i]的情况下,使得A的前i + 1个元素和B的前i + 1个元素严格递增的最小交换次数;
//dp2[i]表示在交换A[i]和B[i]的情况下,使得A的前i + 1个元素和B的前i + 1个元素严格递增的最小交换次数。
class Solution {
public:
    int minSwap(vector& A, vector& B) {
        int length = A.size();
        vector dp1(length, INT_MAX);
        vector dp2(length, INT_MAX);
        dp1[0] = 0;
        dp2[0] = 1;
        for(int i=1; iA[i-1]&&B[i]>B[i-1]){
                dp1[i] = dp1[i-1];
                dp2[i] = dp2[i-1]+1; //需要交换 因此+1
            }
            if(B[i]>A[i-1]&&A[i]>B[i-1]){
                dp1[i] = min(dp1[i], dp2[i-1]);
                dp2[i] = min(dp2[i], dp1[i-1]+1); //如果dp1[i - 1] + 1 < dp2[i],那么说明在维持第i - 1对元素的次序的情况下,交换第i对元素可以达到更优解
            }
        }
        return min(dp1.back(), dp2.back());
    }
};

 

你可能感兴趣的:(leetcode)