Openjudge — 7624 山區建小學

問題描述

政府在某山區修建了一條道路,剛好穿越總共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;
}
相關文章
相關標籤/搜索