1. 程式人生 > >使序列有序的最少交換次數

使序列有序的最少交換次數

題目1:

給出一個序列,只交換相鄰兩數,使得序列升序排列,求出最少交換次數。

思路:

如果說只是交換相鄰兩個數字。那麼最小交換次數就是這個序列的逆序數。

  1. 假設序列個數為n,我們先把最大的數換到最後,因為是相鄰數字交換,所以把最大數交換到最後,需要交換的次數為最大數後的數字個數。
  2. 當完成最大數的交換後,可以將最大數從序列中劃去不管了,即此時序列個數為n-1了,我們再在該序列中找到一個最大數,進行相同操作。
  3. 所以使整個序列有序的交換次數為,這個序列的所有逆序總數。

比如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;
}