1. 程式人生 > >經典問題之最少交換次數

經典問題之最少交換次數

題目簡述:

此類題目一般概括為:通過交換相鄰數來完成排序所需要的最少交換次數。

分析:

顯然簡單的氣泡排序就是基於這種思想,但是O(n^2)的效率常常無法滿足題目要求,我們只能另尋它路。
還是模擬原始做法:

4 8 2 7 5 6 1 3
1 4 8 2 7 5 6 3------>6次

1 2 4 8 7 5 6 3------>2次

1 2 3 4 8 7 5 6------>5次

1 2 3 4 5 8 7 6------>2次

1 2 3 4 5 6 8 7------>2次

1 2 3 4 5 6 7 8------>1次

統計18次

在模擬過程中,我們發現每次都是找到一個最小的然後移到最前面,但是除了這個最小的,其他數的相對次序並沒有改變,所以我們可以將原始做法換一種表述方式:

找到最小的,統計它前面有多少個數比它小,然後加入結果,將這個最小的刪去。

這時我們就發現,其實原題就是求數列的逆序對的個數!
樹狀陣列。線段樹。歸併。


參考程式:

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=210000;
int n,a[maxn],b[maxn];
int bit(int x){
	return x&(-x);
}
void add(int x){
	while (x<=30){
		b[x]++;
		x+=bit(x);
	}
}
int query(int x){
	int tt=0;
	while (x>0){
		tt+=b[x];
		x-=bit(x);
	}
	return tt;
}
int main(){
	freopen("swapping.in","r",stdin);
	freopen("swapping.out","w",stdout);
	char cmd;
	while (scanf("%c",&cmd)==1)
		a[++n]=cmd-'A'+1;
	int ans=0;
	for (int i=1;i<=n;i++){
		add(a[i]);
		ans+=i-query(a[i]);
	}
	printf("%d",ans);
	return 0;
}