• 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
    ",dp[m][n]);
        return 0;
    }
  • 相关阅读:
    LInux常用命令:总结
    SpringBoot声明式事务(转)
    连接linux客户端工具
    查看servlet 3.0文档方法
    通过spring.io找spring历史版本
    归并排序(比希尔还要快)
    快速排序(比希尔排序还要快)
    希尔排序(交换式和移位式)
    插入排序
    选择排序(时间复杂度O(n^2))
  • 原文地址:https://www.cnblogs.com/hua-dong/p/7810865.html
Copyright © 2020-2023  润新知