使序列有序的最少交換次數
題目1:
給出一個序列,只交換相鄰兩數,使得序列升序排列,求出最少交換次數。
思路:
如果說只是交換相鄰兩個數字。那麼最小交換次數就是這個序列的逆序數。
- 假設序列個數為n,我們先把最大的數換到最後,因為是相鄰數字交換,所以把最大數交換到最後,需要交換的次數為最大數後的數字個數。
- 當完成最大數的交換後,可以將最大數從序列中劃去不管了,即此時序列個數為n-1了,我們再在該序列中找到一個最大數,進行相同操作。
- 所以使整個序列有序的交換次數為,這個序列的所有逆序總數。
比如4,3,2,1。
(4,3) (4,2) (4,1),有3個逆序,交換後 3,2,1,4
(3,2) (3,1),有2個逆序,交換後2,1,3,4
(2,1),有1個逆序,交換後1,2,3,4
用歸併的方法求逆序對即可
題目2:
給出一個序列,可交換任意兩個數,使序列升序排列,求最少交換次數。
思路:
與之前不同,之前是隻能交換相鄰兩數。
比如4,3,2,1,如果只交換相鄰兩數,最少交換次數為6。
但如果是交換任意兩數,最少交換次數就為2。
有序列5,4,3,2,1。共5個數。
nums [0] [1] [2] [3] [4]
5 4 3 2 1
按升序排列之後為
nums1 [0] [1] [2] [3] [4]
1 2 3 4 5
我們可以發現5,1雖然不在自己應該在的位置,但是如果把它們兩個看成整體,對於整個序列來說它們佔據了排好序後5,1應該在的位置,所以對於整個序列來說是有序的,它們只是自身內部無序而已。5應該到1處,1應該到5處,形成了一個迴圈,所以可以將它們抽象成一個環,環內換序就可以了。(下面把這種環稱為迴圈節)
對於一個含有n個元素的迴圈節來說,要使其有序,要交換n-1次(前面都排好了,最後一個數自然有序就不用排了)。
上例中3在原本就在的位置,可以看成一個元素的迴圈節。
我們可以推斷出有一個迴圈節,就可以少交換一次,因為n個元素的迴圈節,只需交換n-1次即可有序。
那麼對於整個序列來說,最少交換次數為 元素總數 - 迴圈節個數。
5,4,3,2,1序列中有3個迴圈節,所以最少交換次數為2。
下面是求一個序列的迴圈節的程式碼:
#include<iostream> #include<cstdio> #include<map> #include<algorithm> using namespace std; int a[100], b[100],flag[100]; int main(){ int n, circle = 0; map<int, int> mp; cin >> n; for(int i = 1; i <= n; i++){ scanf("%d",&a[i]); b[i] = a[i]; mp[a[i]] = i; } sort(b+1,b+1+n); for(int j = 1; j <= n; j++){ if(!flag[j]){ int tp = j; while(!flag[tp]){ flag[tp] = 1; tp = mp[b[tp]]; } circle++;//迴圈節大小 } } cout << n - circle;//最小交換次數 return 0; }