#include <iostream> using namespace std; #define MAX 99999 class PostStation { private: int **least;//least[i][j]表示i....n个村庄用j个邮局覆盖的最小距离和 int **cost;//cost[i][j]表示用1个邮局覆盖i....j的最小距离和,邮局位置只有取i到j的中点才会最小,可以证明 int n;//村庄数量 int m;//邮局数量 int *x;//村庄坐标 int **post;//post[i][j]表示i...n个村庄由j个邮局覆盖时的最左邮局 public: void input() { for(int i=1;i<=n;i++) { cout<<"第"<<i<<"个村庄的位置:"; cin>>x[i]; } } PostStation(int n,int m) { this->n=n; this->m=m; least=new int* [n+2];//用least[1 to n+1][0 to m] cost=new int* [n+1];//用cost[1 to n][] x=new int[n+1]; post=new int* [n+2]; for(int i=0;i<n+1;i++) { cost[i]=new int[n+1]; least[i]=new int[m+1]; post[i]=new int[m+1]; } least[n+1]=new int[m+1]; post[n+1]=new int[m+1]; } void getCost() { int middle; for(int i=1;i<=n;i++) { for(int j=i;j<=n;j++) { cost[i][j]=0; middle=(i+j)/2; for(int k=i;k<middle;k++) { cost[i][j]+=x[middle]-x[k]; } for(int k=middle+1;k<=j;k++) { cost[i][j]+=x[k]-x[middle]; } } } } void postStation() { getCost(); for(int i=1;i<=n+1;i++) { for(int j=0;j<=m;j++) { least[i][j]=MAX; } } least[n][1]=0; least[n+1][0]=0; for(int i=n-1;i>=1;i--) { for(int j=1;j<=m;j++) { for(int k=0;k<=n-i;k++) { if(cost[i][i+k]+least[i+k+1][j-1]<least[i][j]) { least[i][j]=cost[i][i+k]+least[i+k+1][j-1]; post[i][j]=k; } } } } cout<<least[1][m]<<endl; } }; void main() { PostStation test(10,5); test.input(); test.postStation(); }
least[i][m]=min(cost[i][i+k]+least[i+k+1][m-1]) 0 <=k <=n-i
least[n][1]=0 least[n+1][0]=0
least[i][m] 表示从用m个邮局覆盖i......n个村庄
用m个邮局覆盖i.....n个村庄的距离和 等于用第一个邮局覆盖 i 到i+k 个村庄的距离和+ 用m-1个邮局覆盖i+k+1......n个村庄的距离和
由于用1个邮局覆盖i 到 j 这么多村庄的最小花费一定是将邮局放置在 (i+j)/2 这个村庄的位置, 具体可以证明一下.
所以我们用O(n3)时间去计算cost[i][j], 然后用O(n2)时间去计算least[i][m].
我们最终想要的是least[i][m],即用m个邮局覆盖i.....n个村庄的最小距离.