超市规划
时间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
小Hi居住的城市的中轴线恰好是一条马路。沿着这条马路一共坐落有N个居民小区,其中第i个小区距离马路一端的距离是Ai。
现在市政厅决定在这条马路沿线修建K个超市(可以修建在任意实数位置),并且定义“不方便指数”是所有小区与最近的超市距离的平方之和。
当然市政厅希望“不方便指数”越小越好,请求出该指数的最小值。
输入
第一行包含两个整数N和K。
以下N行每行包含一个整数Ai。
对于50%的数据,1 ≤ N, K ≤ 100
对于100%的数据,1 ≤ N, K ≤ 2000 0 ≤ Ai ≤ 100000
输出
一个实数代表指数的最小值,保留3位小数。
- 样例输入
-
4 2 1 2 3 4
- 样例输出
-
1.000
有O(n^3)的解法,先知道两个结论:
1,i-j的点中找一个点,使得它到其他点的距离和最小,则这个点可以是下标为中值的点(也有可能一条线段,但是这个点再这条线段上,也满足),即x0=x[(i+j)/2];可以用反证法来证明。
2,i-j的线段上找一个点,使得它到其他点的距离平方的和最小,则这个点是几何中点,即x0=(xi+..xj)/(j-i+1);可以求偏导数来证明。
由2结论,我们得到如下代码:
其中c[i][j]是i到j中有一个超市时的最优解
则有O(n^3):
for(i=1;i<=n;i++) for(j=i+1;j<=n;j++){ double mid=(sum[j]-sum[i-1])/(j-i+1); for(k=i;k<=j;j++){ c[i][j]+=(a[i]-mid)*(a[i]-mid); } }
化简:c[i][j]=Σa[i]*a[i]+mid*mid-2*mid*a[i],则有O(n^2):
for(i=1;i<=n;i++) for(j=i+1;j<=n;j++){ double mid=(suma[j]-suma[i-1])/(j-i+1); c[i][j]=(summ[j]-summ[i-1])+(j-i+1)*mid*mid-2*mid*(suma[j]-suma[i-1]); }
dp[i][j]表示前j个点有i个超市的最优解,则有O(nnk):
for(i=1;i<=n;i++) dp[1][i]=c[1][i]; for(i=2;i<=m;i++){ s[i][n+1]=n; for(j=n;j>=1;j--){ for(k=1;k<=j;k++) if(dp[i][j]>dp[i-1][k]+c[k+1][j]){ s[i][j]=k; dp[i][j]=dp[i-1][k]+c[k+1][j]; } } }
四边形不等式优化:则有O(nk);
for(i=1;i<=n;i++) dp[1][i]=c[1][i]; for(i=2;i<=m;i++){ s[i][n+1]=n; for(j=n;j>=1;j--){ for(k=s[i-1][j];k<=s[i][j+1];k++) if(dp[i][j]>dp[i-1][k]+c[k+1][j]){ s[i][j]=k; dp[i][j]=dp[i-1][k]+c[k+1][j]; } } }
所以O(n^2)就okey辣,鸡冻!
#include<cstdio> #include<cstdlib> #include<iostream> #include<algorithm> #include<cstring> #include<cmath> using namespace std; const int maxn=2010; const double inf=100000000000000000; double dp[maxn][maxn],c[maxn][maxn]; double a[maxn],suma[maxn],summ[maxn],s[maxn][maxn]; int main() { int n,i,j,k,m; scanf("%d%d",&n,&m); for(i=1;i<=n;i++) { scanf("%lf",&a[i]); suma[i]=suma[i-1]+a[i]; summ[i]=summ[i-1]+a[i]*a[i]; } sort(a+1,a+n+1); for(i=1;i<=n;i++) { suma[i]=suma[i-1]+a[i]; summ[i]=summ[i-1]+a[i]*a[i]; } for(i=1;i<=n;i++) for(j=i+1;j<=n;j++){ double mid=(suma[j]-suma[i-1])/(j-i+1); c[i][j]=(summ[j]-summ[i-1])+(j-i+1)*mid*mid-2*mid*(suma[j]-suma[i-1]); } for(j=1;j<=m;j++) for(i=1;i<=n;i++) dp[j][i]=inf; for(i=1;i<=n;i++) dp[1][i]=c[1][i]; for(i=2;i<=m;i++){ s[i][n+1]=n; for(j=n;j>=1;j--){ for(k=s[i-1][j];k<=s[i][j+1];k++) if(dp[i][j]>dp[i-1][k]+c[k+1][j]){ s[i][j]=k; dp[i][j]=dp[i-1][k]+c[k+1][j]; } } } printf("%.3lf ",dp[m][n]); return 0; }