http://poj.org/problem?id=1160
题意:有N个村庄,要在这N个村庄中建K个邮局,求如何布置这些邮局能使得所有村庄到离它最近的那个邮局的距离总和最小。
此题个人认为与最长递增子序列有一定的类似,我正是受了最长递增子序列解题方法的启发,才A出了此题,但貌似不能通过优化最长递增子序列的方法(nlgn)来优化它
///v个村庄,p个邮局,把邮局建在哪些村庄里才能使所有村庄到离家最近的邮局的总路径最短
///
#include <iostream>
#include <algorithm>
#define MAX 300 //村庄的最大个数
using namespace std;
int vill[MAX+10]; //每个村庄的坐标,严格递增数组
int dp[MAX+10][32]; //dp[i][j] 表示最后一个邮局在村庄 i ,一共建了 j 个邮局
int INF=4000000;
int m[32]; //m[j] 表示邮局总数为 j 时最短距离
//二分查找,查找到两个村庄之间与它们距离相等的村庄
int binarySearch(int s,int e,int data){
if(s<=e){
int mid=(s+e)/2;
if(vill[mid]==data)
return mid;
if(vill[mid]<data)
s=mid+1;
else e=mid-1;
return binarySearch(s,e,data);
}
}
int main()
{
int v,p;
scanf("%d%d",&v,&p);
int i,j,k;
for(i=0;i<v;i++){
scanf("%d",&vill[i]);
}
memset(dp,0,sizeof(dp));
//初始化只建一个邮局时,该邮局建在各个村庄时的总路径
for(i=0;i<v;i++){
dp[i][1]=0;
for(k=0;k<i;k++){
dp[i][1]+=vill[i]-vill[k];
}
for(k=i;k<v;k++){
dp[i][1]+=vill[k]-vill[i];
}
}
for(i=1;i<v;i++){
for(k=0;k<=p;k++)
m[k]=INF;
for(k=0;k<i;k++){
int mid=binarySearch(k,i,(vill[i]-vill[k])/2);
int sum=0;
while(vill[mid]-vill[k]<=vill[i]-vill[mid])
mid+=1;
//求出村庄 i 和 k 之间的村庄到邮局的总路径因为在 i 新建了一个邮局后的改变量
while(mid!=i){
sum-=2*vill[mid]-vill[k]-vill[i];
mid++;
}
//求出村庄 i 之后的村庄到邮局的总路径因为在 i 新建了一个邮局后的改变量,为负
sum-=(vill[i]-vill[k])*(v-i);
//更新 m 数组
for(j=1;j<=k+1&&j<=p;j++){
if(dp[k][j]+sum<m[j])
m[j]=dp[k][j]+sum;
}
}
//状态转移方程 dp[i][k]=min{dp[j][k],j<i}
for(k=2;k<=i+1&&k<=p;k++)
dp[i][k]=m[k-1];
}
int minn=INF;
for(i=p-1;i<v;i++){
if(minn>dp[i][p])
minn=dp[i][p];
}
cout<<minn<<endl;
return 0;
}