Live2d Test Env

HihoCoder1621 : 超市规划(四边形DP优化)()

 超市规划

时间限制: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\n",dp[m][n]);
    return 0;
}

 

posted @ 2017-11-09 19:15  nimphy  阅读(270)  评论(0编辑  收藏  举报