政府在某山區修建了一條道路,剛好穿越總共m個村莊的每一個村莊一次,沒有迴路或交叉,任意兩個村莊只能經過這條路來往。已知任意兩個相鄰的村莊之間的距離爲di(爲正整數),其中,0 < i < m。爲了提升山區的文化素質,政府又決定從m個村中選擇n個村建小學(設 0 < n < = m < 500 )。請根據給定的m、n以及全部相鄰村莊的距離,選擇在哪些村莊建小學,才使得全部村到最近小學的距離總和最小,計算最小值。ios
第1行爲m和n,其間用空格間隔
第2行爲(m-1) 個整數,依次表示從一端到另外一端的相鄰村莊的距離,整數之間以空格間隔。
例如
10 3
2 4 6 5 2 4 3 1 3
表示在10個村莊建3所學校。第1個村莊與第2個村莊距離爲2,第2個村莊與第3個村莊距離爲4,第3個村莊與第4個村莊距離爲6,…,第9個村莊到第10個村莊的距離爲3。數組
各村莊到最近學校的距離之和的最小值。code
10 2
3 1 3 1 1 1 1 1 3ci
18
來源
元培-From Whfstring
動態規劃來解決,這裏須要一個輔助的數組dist,dist[i][j]表示在從i到j這一段區間建一所小學,i到j的村莊都到這個學校來上學的路程和。
狀態表達:f[i][j]表示前i個村莊建j所學校,到裏那個村莊最近的學校上學的路程和。
狀態轉移:f[i][j] = std::min(f[i][j],f[k][j-1]+dist[k+1][i]),也就是:it
for(int i = 1;i<=n;i++) for(int j = 1;j<i;j++) for(int k = 1;k<i;k++) f[i][j] = std::min(f[i][j],f[k][j-1]+dist[k+1][i]);
狀態數量:n^2
轉移代價:O(n)
時間複雜度:O(n^3)
空間複雜度:O(n^2)io
還有一個關鍵點,就是有關如何求dist數組的,其實,咱們能夠發現,在i到j村莊裏建小學,選i到j村莊的路程的中間位置村莊,必定是最優的。畫個圖你就能發現了。
如何快速求dist呢,其實,dist[i][j]等於dist[i][j-1]+num[j]-num[i+j]/2的,建議畫個圖。stream
#include<iostream> #include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring> int num[1001],dist[1001][1001],f[1001][1001]; int main() { int n,m; std::cin>>n>>m; for(int i = 2;i<=n;i++){std::cin>>num[i];num[i] += num[i-1];} for(int i = 1;i<=n;i++) for(int j = i;j<=n;j++) dist[i][j] = dist[i][j-1]+num[j]-num[(i+j)/2]; memset(f,0x3f,sizeof(f)); for(int i = 1;i<=n;i++)f[i][1] = dist[1][i]; for(int i = 1;i<=n;i++) for(int j = 1;j<i;j++) for(int k = 1;k<i;k++) f[i][j] = std::min(f[i][j],f[k][j-1]+dist[k+1][i]); std::cout<<f[n][m]; return 0; }