題目鏈接:
題目
政府在某山區修建了一條道路,恰好穿越總共個村莊的每個村莊一次,沒有迴路或交叉,任意兩個村莊只能通過這條路來往。已知任意兩個相鄰的村莊之間的距離爲(爲正整數),其中,。爲了提高山區的文化素質,政府又決定從個村中選擇個村建小學。請根據給定的、以及所有相鄰村莊的距離,選擇在哪些村莊建小學,才使得所有村到最近小學的距離總和最小,計算最小值。
輸入
第行爲和,其間用空格間隔。
第2行爲個整數,依次表示從一端到另一端的相鄰村莊的距離,整數之間以空格間隔。
表示在個村莊中建所學校。第個村莊與第個村莊距離爲,第個村莊與第個村莊距離爲,第個村莊與第個村莊距離爲,,第個村莊到第個村莊的距離爲。
輸出
各村莊到最近學校的距離之和的最小值。
樣例輸入
10 2
3 1 3 1 1 1 1 1 3
樣例輸出
18
數據範圍
思路
這道題是一道動態規劃。
要做出這道題,最重要的一部是怎麼求出到第之間的所有村莊都到一個學校時,所需要的距離總和的最小值。
那麼在這裏呢,我們就認爲最中間的村莊就是可以讓距離總和最小的。我們用表示,在第個村莊到第個村莊中只建一個學校所能獲得的最小距離總和。那麼我們就可以通過前綴和和我們的貪心求出。
接着呢,我們就可以通過求出答案。至於怎麼呢,很簡單,就是這個:
其中,表示我們之前算出來的距離,而就表示分界點。
這個其實就是一個算法,只是表示的方法不一樣,而且因爲最大隻有,所以三重循環是可行的。
代碼
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n, m, a[501], dis[501][501], f[501][501];
int main() {
scanf("%d %d", &n, &m);//讀入
for (int i = 2; i <= n; i++) {
scanf("%d", &a[i]);//讀入
a[i] += a[i - 1];//前綴和
}
for (int i = 1; i <= n; i++)
for (int j = i; j <= n; j++) {
int school = (i + j) >> 1;//應該建的位置
for (int k = i; k <= j; k++)
dis[i][j] += abs(a[school] - a[k]);//求出距離
}
memset(f, 0x7f, sizeof(f));//初始化
f[0][0] = 0;//初始化
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
if (j > i) {
f[i][j] = 0;//每個地方都可以建學校
continue;
}
for (int k = j - 1; k <= i; k++)
f[i][j] = min(f[i][j], f[k][j - 1] + dis[k + 1][i]);//動態轉移方程
}
printf("%d", f[n][m]);//輸出
return 0;
}