JDOJ-1008:牛排序
1008: 牛排序
Time Limit: 2 Sec Memory Limit: 64 MBSubmit: 120 Solved: 52
[Submit][Status][Web Board]
Description
農夫JOHN準備把他的 N(1 <= N <= 10,000)頭牛排隊以便於行動。因為脾氣大的牛有可能會搗亂,JOHN想把牛按脾氣的大小排序。每一頭牛的脾氣都是一個在1到100,000之間的整數並且沒有兩頭牛的脾氣值相同。在排序過程中,JOHN可以交換任意兩頭牛的位置。因為脾氣大的牛不好移動,JOHN需要X+Y秒來交換脾氣值為X和Y的兩頭牛。
請幫JOHN計算把所有牛排好序的最短時間。
Input
第1行: 一個數, N。
第2~N+1行: 每行一個數,第i+1行是第i頭牛的脾氣值。
Output
第1行: 一個數,把所有牛排好序的最短時間。
Sample Input
3 2 3 1Sample Output
7HINT
【樣例說明】 隊列裏有三頭牛,脾氣分別為 2,3, 1。
2 3 1 : 初始序列
2 1 3 : 交換脾氣為3和1的牛(時間=1+3=4).
1 2 3 : 交換脾氣為1和2的牛(時間=2+1=3).
總結:置換群裸題,找出每個循環,在兩種情況下去最小值,以下引自hzwer
1.找出初始狀態和目標狀態。明顯,目標狀態就是排序後的狀態。
2.畫出置換群,在裏面找循環。例如,數字是8 4 5 3 2 7
明顯,目標狀態是2 3 4 5 7 8,能寫為兩個循環:
(8 2 7)(4 3 5)。
3.觀察其中一個循環,明顯地,要使交換代價最小,應該用循環裏面最小的數字2,去與另外的兩個數字,7與8交換。這樣交換的代價是:
sum – min + (len – 1) * min
化簡後為:
sum + (len – 2) * min
其中,sum為這個循環所有數字的和,len為長度,min為這個環裏面最小的數字。
4.考慮到另外一種情況,我們可以從別的循環裏面調一個數字,進入這個循環之中,使交換代價更小。例如初始狀態:
1 8 9 7 6
可分解為兩個循環:
(1)(8 6 9 7),明顯,第二個循環為(8 6 9 7),最小的數字為6。我們可以抽調整個數列最小的數字1進入這個循環。使第二個循環變為:(8 1 9 7)。讓這個1完成任務後,再和6交換,讓6重新回到循環之後。這樣做的代價明顯是:
sum + min + (len + 1) * smallest
其中,sum為這個循環所有數字的和,len為長度,min為這個環裏面最小的數字,smallest是整個數列最小的數字。
5.因此,對一個循環的排序,其代價是sum – min + (len – 1) * min和sum + min + (len + 1) * smallest之中小的那個數字。
#include<bits/stdc++.h> using namespace std; const int maxn = 100005; int pos[maxn], n, a[maxn], b[maxn], tot; bool vis[maxn]; int cnt = 0, ans = 0, minx = 10000007; void dfs(int now) { if(vis[now]) { ans += min(tot + (cnt - 2) * minx, tot + minx + (cnt + 1) * b[1]); return; } cnt++; tot += a[now]; vis[now] = 1; minx = min(minx, a[now]); dfs(pos[a[now]]); } int main() { scanf("%d", &n); for (int i = 1; i <= n; ++i) scanf("%d", &a[i]), b[i] = a[i]; sort(b + 1, b + n + 1); for (int i = 1; i <= n; ++i) pos[b[i]] = i; for (int i = 1; i <= n; ++i) { if(vis[i] == 0) { minx = 10000007; cnt = tot = 0; dfs(i); } } printf("%d\n", ans); return 0; }
JDOJ-1008:牛排序