给定有序数组a[1...n]的一个置换a[σ(1)...σ(n)], 通过交换数组元素把置换后的数组恢复为有序,
定义进行一次交换的代价为两元素之和,试问此过程的最小总代价。
实际上一种置换即定义S = {1,...,n}到其自身的一个双射函数f。
可以证明必然存在整数k使得f^k = f0 = I。即存在周期性。
实际上此函数的幂存在局部周期性,即存在若干个互不相交循环单位,S中每个元素恰属于其中一个循环节。
例如考虑:f:u(1,2,3,4,5,6,7) -> v(3,6,7,5,1,2,4)
将循环单位极小化:(3,7,5,1,4)(6,2)。
循环节内的元素按其在v中的位置排列。
我们的目标是将其调整为(1,3,4,5,7)(2,6)。
循环节内的每一个元素都在不合适的位置上,因此长度为l循环节内部至少需要进行(l - 1)次互换可使其有序。
考虑长度为l的循环节:
由于所有元素均需要调整,因此循环节内部调整代价的一个下界是循环节内所有元素之和加上最小元素与交换次数的乘积。
可以达到该下界:
例考虑调整(3,7,5,1,4),由于1占据了5应该所处的位置,(5,1)交换得到(3,7,1,5,4)。
如此不断迭代将其整理为有序所需代价为min_value * (l - 2) + sum_value(*)。
由于循环节内部调整相互独立,即可以分步进行,考虑利用非循环节内部元素进一步降低代价。
交换循环节外部一元素a(取最小的元素显然更优)和循环节内部一元素b,如此总代价为:
2 * (a + b) + a * (l - 2) + sum_value - b + a = b + a * (l + 1) + sum_value(#),
由于a取外部最小值,b取内部最小值,(#)可能会优于(*)。
(#)-(*) = a * (l + 1) - (l - 3) * b = (l + 1) * (a - b) + 4 * b,
由于已经引入全局最小值,若再次交换则必有a > b,只会增加代价。
因此最多交换一次。
这样本题就可以给出答案了。
http://poj.org/problem?id=3270
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 const int maxn = 1e4 + 10; 6 const int inf = 0x3f3f3f3f; 7 int f[maxn]; 8 int a[maxn], n, mini; 9 bool vis[maxn]; 10 11 void solve(){ 12 memset(vis, 0, sizeof vis); 13 int ans = 0; 14 for(int i = 0; i < n; i++){ 15 if(vis[i]) continue; 16 vis[i] = 1; 17 int len = 1, minii = a[i], sum = a[i]; 18 int j = f[i]; 19 while(j != i){ 20 vis[j] = 1; 21 sum += a[j]; 22 ++len; 23 minii = min(minii, a[j]); 24 j = f[j]; 25 } 26 ans += (len - 2) * minii + sum + min(0, (len + 1) * (mini - minii) + 4 * minii); 27 } 28 printf("%d ", ans); 29 } 30 31 bool cmp1(int u, int v) { return a[u] < a[v]; } 32 33 int main(){ 34 freopen("in.txt", "r", stdin); 35 while(~scanf("%d", &n)){ 36 mini = inf; 37 for(int i = 0; i < n; i++){ 38 scanf("%d", &a[i]); 39 mini = min(a[i], mini); 40 } 41 for(int i = 0; i < n; i++) f[i] = i; 42 sort(f, f + n, cmp1); 43 solve(); 44 } 45 return 0; 46 }