题意整理
- 给定两个同样大小的数列,现在要将第一个变为严格降序,第二个变为严格升序。
- 可以交换两个数列同一列的元素,问最少交换多少次。
- 如果无法交换成严格有序,返回-1。
方法一(动态规划)
1.解题思路
- 状态定义:表示当前无交换的最少交换次数,表示当前有交换的最少交换次数。
- 状态初始化:当只有一个元素时,无交换的最少交换次数显然为0,有交换的最少交换次数显然为1。
- 状态转移:先将当前位置的状态设置为不可达,然后分四种情况进行讨论。如果并且,不需交换,用前一次无交换的最小次数跟新当前无交换的状态;如果并且,i-1处需要交换,用前一次有交换的最小次数跟新当前无交换的状态;如果并且,当前处需要交换,用前一次无交换的最小次数加1跟新当前有交换的状态;如果并且,i-1处和当前处都需要交换,用前一次有交换的最小次数加1跟新当前有交换的状态。
- 得到最后一个下标的两个状态值,取较小值即可。如果是不可达状态,返回-1,否则返回较小的状态值。
2.代码实现
import java.util.*; public class Solution { /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * 计算最小交换次数 * @param firstRow int一维数组 第一行的数列数据 * @param secondRow int一维数组 第二行的数列数据 * @return int */ //最大Integer型整数的一半 final int INF=Integer.MAX_VALUE/2; public int arrange (int[] firstRow, int[] secondRow) { int n=firstRow.length; //dp[i][0]表示当前无交换的最少交换次数,dp[i][1]表示当前有交换的最少交换次数 int[][] dp=new int[n][2]; //初始化 dp[0][0]=0; dp[0][1]=1; for(int i=1;i<n;i++){ //设置为不可达状态 dp[i][0]=INF; dp[i][1]=INF; //如果不需交换,跟新dp[i][0] if(firstRow[i]<firstRow[i-1]&&secondRow[i]>secondRow[i-1]){ dp[i][0]=Math.min(dp[i][0],dp[i-1][0]); } //如果i-1处需要交换,跟新dp[i][0] if(firstRow[i]<secondRow[i-1]&&secondRow[i]>firstRow[i-1]){ dp[i][0]=Math.min(dp[i][0],dp[i-1][1]); } //如果当前处需要交换,跟新dp[i][1] if(secondRow[i]<firstRow[i-1]&&firstRow[i]>secondRow[i-1]){ dp[i][1]=Math.min(dp[i][1],dp[i-1][0]+1); } //如果i-1处和当前处都需要交换,跟新dp[i][1] if(firstRow[i]>firstRow[i-1]&&secondRow[i]<secondRow[i-1]){ dp[i][1]=Math.min(dp[i][1],dp[i-1][1]+1); } } //取较小者 int min=Math.min(dp[n-1][0],dp[n-1][1]); if(min==INF) return -1; return min; } }
3.复杂度分析
- 时间复杂度:只需线性遍历数组一次,所以时间复杂度为。
- 空间复杂度:需要额外大小为的dp数组,所以空间复杂度为。
方法二(空间优化)
1.解题思路
此方法和方法一的思路相同,分析可发现,每一次的状态值只与前一次的两个状态值有关,所以可能使用临时变量的方式先计算出跟新值,再用跟新值替换掉旧值,从而达到记录所有状态的目的。
动图展示:
2.代码实现
import java.util.*; public class Solution { /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * 计算最小交换次数 * @param firstRow int一维数组 第一行的数列数据 * @param secondRow int一维数组 第二行的数列数据 * @return int */ //最大Integer型整数的一半 final int INF=Integer.MAX_VALUE/2; public int arrange (int[] firstRow, int[] secondRow) { int n=firstRow.length; //初始化 int nochange=0; int change=1; for(int i=1;i<n;i++){ //设置为不可达状态 int tmp1=INF; int tmp2=INF; //如果不需交换,跟新dp[i][0] if(firstRow[i]<firstRow[i-1]&&secondRow[i]>secondRow[i-1]){ tmp1=Math.min(tmp1,nochange); } //如果i-1处需要交换,跟新dp[i][0] if(firstRow[i]<secondRow[i-1]&&secondRow[i]>firstRow[i-1]){ tmp1=Math.min(tmp1,change); } //如果当前处需要交换,跟新dp[i][1] if(secondRow[i]<firstRow[i-1]&&firstRow[i]>secondRow[i-1]){ tmp2=Math.min(tmp2,nochange+1); } //如果i-1处和当前处都需要交换,跟新dp[i][1] if(firstRow[i]>firstRow[i-1]&&secondRow[i]<secondRow[i-1]){ tmp2=Math.min(tmp2,change+1); } nochange=tmp1; change=tmp2; } //取较小者 int min=Math.min(nochange,change); if(min==INF) return -1; return min; } }
3.复杂度分析
- 时间复杂度:只需线性遍历数组一次,所以时间复杂度为。
- 空间复杂度:需要额外常数级别的空间,所以空间复杂度为。