题目大意:有v个村庄成直线排列,要建设p个邮局,为了使每一个村庄到离它最近的邮局的距离之和最小,应该怎样分配邮局的建设,输出最小距离和。
题目分析:定义状态dp(i,j)表示建设 i 个邮局最远覆盖到第 j 个村庄时最小距离和。容易得到dp(i,j)=min(dp(i-1,k-1)+w(k,j)),其中w(k,j)表示在k~j之间建设一个邮局的最小距离,所以很显然w(i,j)关于包含关系单调,可以看出w(i,j)还满足凸四边形不等式,所以dp(i,j)也满足凸四边形不等式。那么就有K(i,j-1)<=K(i,j)<=K(i+1,j),也就能通过限定k的取值范围达到优化的效果。其实这道题数据规模不大,不加优化也能AC。
代码如下:
# include<iostream>
# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;
const int INF=1<<30;
int v,p;
int dp[305][305];
int K[305][305];
int x[305],s[305];
void read()
{
s[0]=0;
for(int i=1;i<=v;++i){
scanf("%d",x+i);
s[i]=x[i]+s[i-1];
}
}
int getw(int a,int b)
{
int m=(a+b)>>1;
return s[b]-s[m]-x[m]*(a+b-2*m)-s[m-1]+s[a-1];
}
void solve()
{
for(int i=1;i<=v;++i){
dp[i][i]=0;
dp[0][i]=INF;
K[i][i]=i;
}
for(int l=2;l<=v-p+1;++l){
for(int i=1;i+l-1<=v;++i){
int j=i+l-1;
dp[i][j]=INF;
int temp;
for(int k=K[i][j-1];k<=K[i+1][j];++k){
if(dp[i][j]>(temp=dp[i-1][k-1]+getw(k,j))){
dp[i][j]=temp;
K[i][j]=k;
}
}
}
}
printf("%d\n",dp[p][v]);
}
int main()
{
while(~scanf("%d%d",&v,&p))
{
read();
solve();
}
return 0;
}