题目大意:有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 ",dp[p][v]); } int main() { while(~scanf("%d%d",&v,&p)) { read(); solve(); } return 0; }