[USACO07FEB] Cow Sorting G
阿新 • • 發佈:2021-07-03
Desprition
農夫 \(JOHN\) 準備把他的 \(N\) 頭牛排隊以便於行動。因為脾氣大的牛有可能會搗亂,\(JOHN\) 想把牛按脾氣的大小排序。在排序過程中,\(JOHN\) 可以交換任意兩頭牛的位置。因為脾氣大的牛不好移動,\(JOHN\) 需> 要 \(X+Y\) 秒來交換脾氣值為 \(X\) 和 \(Y\) 的兩頭牛。 請幫 \(JOHN\) 計算把所有牛排好序的最短時間。
Solution
首先,因為起始順序和最後的位置順序都是固定的,所以置換是確定的 \(\begin{pmatrix}a_i&a_{i+1}&a_{i+2}&……&a_n\\b_i&b_{i+1}&b_{i+2}&……&b_n\end{pmatrix}\)
找出所有輪換(迴圈節)。
對於每個輪換來說,其所包含的牛肯定是在輪換內部交換,每頭牛至少交換一次。但是最小花費卻並不能因此確定……因為無法保證輪換內的最小花費就是整個序列的最小花費。
有兩種情況:
- 僅限輪換內部交換;
貪心可知每次交換都應該是脾氣之最小的牛和另外一頭牛交換。一遍一遍換,知道排好順序。
- 有輪換外的牛參與交換。
其實就是將輪換外脾氣值最小的牛 \(x\) 換進來,每次參與交換,輪換內的牛排好位置之後再把 \(x\) 換出去。
Code
#include<cstdio> #include<vector> #include<algorithm> using namespace std; #define ll long long const int N = 1e5 + 5, inf = 1e5; int n,a[N],b[N],id[N],bel[N],tot,mi,sz; ll ans; vector<int> G[N]; int main() { scanf("%d",&n); for(int i = 1; i <= n; i ++) scanf("%d",&a[i]), b[i] = a[i]; sort(b + 1,b + 1 + n); for(int i = 1; i <= n; i ++) id[b[i]] = i; int node,nex; for(int i = 1; i <= n; i ++) {//求輪換 node = id[a[i]]; if(bel[node]) continue; nex = id[a[node]], bel[node] = ++tot; while(nex != node) { bel[nex] = tot; nex = id[a[nex]]; } } for(int i = 1; i <= n; i ++) G[bel[id[a[i]]]].push_back(a[i]); for(int i = 1; i <= tot; i ++) { mi = inf, sz = G[i].size(); for(int j = 0; j < sz; j ++) { ans += G[i][j], mi = min(mi,G[i][j]);//每頭牛都至少交換一次。 } ans -= mi, ans += min((sz - 1) * mi,b[1] * (sz + 1) + mi * 2); //上述兩種情況取最小值。 } printf("%lld",ans); return 0; }