小朋友學演算法(18):交換機器的最小代價
一、問題描述
有N臺機器重量各不相等,現在要求把這些機器按照重量排序,重量從左到右依次遞增。移動機器只能做交換操作,但交換機器要花費一定的費用,費用的大小就是交換機器重量的和。例如:3 2 1,交換1 3後為遞增排序,總的交換代價為4。
給出N臺機器的重量,求將所有機器變為有序的最小代價(機器的重量均為正整數)。
輸入
第1行:1個數N,表示機器及房間的數量。(2 <= N <= 50000)
第2 - N + 1行:每行1個數,表示機器的重量Wi。(1 <= Wi <= 10^9)
輸出
最小代價
樣例1輸入
樣例1輸出
二、思路
以樣例1例,先進行排序
下標 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
排序前 | 1 | 8 | 9 | 7 | 6 |
排序後 | 1 | 6 | 7 | 8 | 9 |
我們從元素1開始看,排序後元素1的位置還是1,那麼就給1到1之間連一條邊,形成一個自環;再到元素8,元素8排序以後到了第4個位置,而第四個位置是元素7,所以給8到7之間連一條有向邊,同理連完剩下的邊可以得到一張圖:

1.png
那麼我們可以發現兩個環,那麼我們回到題目中來,要使最後的總和最小,我們的貪心思路是什麼?
策略一:
對於每一個環的貪心思路就是,找到這個環中最小的那個點,也就是6,然後從6開始進行交換,6和9交換,可以使9到對應的位置,花費為6+9=15,然後6和7交換,花費為6+7=13,最後等到交換完畢,自最後的答案是什麼呢?就是:
(6+9)+(6+7)+(6+8) = (6+7+8+9)+6∗2 = 30+12 = 42。
剩下一個環不用交換,那麼當前的最小值就是42,但是這不一定是最優解。
這種策略的解可表示為ans1 = sum + min * (cnt - 1),這裡min是當前環中的最小值,cnt是min與別的元素交換的次數。
策略二:

2.png
在這個圖中找到一個最小的值,然後用這個值跟著當前的環進行交換,在這個圖中很明顯是1,我們讓第1和第二個環中的最小值6進行交換,然後再像上面一樣,交換1和9,花費為:1+9=10,交換1和7,花費為:1+7=8等到交換完畢,最後的結果是:
(1+6)+(1+9)+(1+7)+(1+8)+(1+6) = (6+8+7+9)+1∗5+6 = 41
這種策略的解可表示為ans2 = sum + least * (cnt + 2) + min,這裡least表示所有元素的最小值,min表示當前環中的最小值。
我們的貪心策略就是在這兩個策略之間,找出一個最小值ans = min(ans1, ans2)。
三、程式碼
#include<iostream> #include<algorithm> #define MAXN 50010 using namespace std; bool visited[MAXN]; //記錄該位置的機器是否已經排好序 int least;//記錄最小重量 struct machine { int origin;//原來的位置 int weight;//機器的重量 }; machine mac[MAXN]; bool cmp(machine a, machine b) { return a.weight < b.weight; } long long solve(int i) { visited[i] = true; int MIN = mac[i].weight; long long sum = mac[i].weight; int j = mac[i].origin; int cnt = 0; while(i != j) { sum += mac[j].weight; MIN = min(MIN,mac[j].weight); visited[j] = true; j = mac[j].origin; cnt++; //計算需要交換的機器數量 } return sum + min((long long)MIN*(cnt-1), (long long)least*(cnt+2)+MIN);//兩種策略的比較 } int main() { int n; long long ans=0; cin>>n; for(int i=1;i<=n;i++) { cin>>mac[i].weight; mac[i].origin=i; } sort(mac+1, mac+n+1, cmp); least = mac[1].weight; for(int i=1; i<=n; i++) { if(!visited[i]) { ans += solve(i); } } cout << ans << endl; return 0; }
少兒程式設計、演算法諮詢請加微信307591841或QQ群581357582

資訊學競賽公眾號.jpg