题意:在一条直线上,有V个村庄,给出这些村庄的坐标。现在要求在某些村庄中建P个邮局,使这些村庄到这些邮局的总的距离最短。
思路:首先,我们考虑如果这V个村庄建一个邮局。这是个很经典的问题,答案也很简单,就是建在这些村庄的重心。
所以对于多个邮局,我们有dp方程:dp[i][j]表示前i个村庄放置j个邮局,最小的总的距离。cost[i][j]表示在村庄i和j之间建一个邮局的最小费用。
则: dp[i][j] = min (dp[k][j-1] + cost[k+1][i]) (1 <= k <= i)。
代码如下:
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAXP = 1000;
const int MAXV = 500;
int V,P;
int a[MAXV];
int dis[MAXV][MAXV];
int dp[MAXV][MAXP];
int main(void)
{
while(scanf("%d %d", &V,&P) != EOF){
for(int i = 1 ; i <= V; ++i)
scanf("%d", &a[i]);
memset(dis,0,sizeof(dis));
for(int i = 1; i <= V;++i)
for(int j = i; j <= V; ++j){
int mid = (i + j) / 2;
for(int k = i ; k <= mid; ++k)
dis[i][j] += a[mid] - a[k];
for(int k = j; k >= mid; --k)
dis[i][j] += a[k] - a[mid];
}
memset(dp,0x3f,sizeof(dp));
for(int i = 1; i <= V; ++i)
dp[i][1] = dis[1][i];
for(int i = 2; i <= P; ++i){
for(int j = i; j <= V; ++j)
for(int k = i - 1; k < j; ++k)
dp[j][i] = min(dp[j][i],dp[k][i-1] + dis[k+1][j]);
}
printf("%d\n",dp[V][P]);
}
return 0;
}