题意:在一条直线上,有N个村庄,给出每个村庄的坐标。现在希望在某些村庄上建P个邮局,使这N个村庄到邮局的总距离最短。
思路:有一个结论这里要用到:如果这N个村庄只建一个邮局的话,那这个邮局建在中间的村庄可以使其他所有村庄到邮局的总距离最短。
因为最多只有300个村庄,我们先用O(N^2)处理出来任意两端内,如果要建一个邮局的话,得到的最短距离。
之后,我们就能进行动态规划了。
用dp[i][j]表示前i个村庄建立了j个邮局所得到的最短总距离。这样,dp[i][j] = min(dp[i][j],dp[k][j-1]+dis[k+1][j])。
代码如下:
#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;
}