zoukankan      html  css  js  c++  java
  • 洛谷P4767 [IOI2000]邮局

    题目链接:P4767 [IOI2000]邮局
    题目大意:

    整数轴上有 (V) 个村庄,要在其中一些村庄中建 (P) 个邮局,求每个村庄和最近的邮局之间所有距离的总和的最小值。
    (1leq Pleq 300, Pleq Vleq 3000)

    思路:
    (val(i,j)) 为在 ([j,i]) 区间上建一个邮局后该区间距离和的最小值,设 (dp[i][j]) 为在 ([1,j]) 上建了 (i) 个邮局时的答案,有朴素 (O(V^2P)) 转移:

    [dp[i][j]=min_{0leq k<j}{dp[i-1][k]+val(j,k+1)} ]

    计算 (val(i,j)) :,记 (s[i]) 为前 (i) 个村庄坐标和,即 (s[i]=sum_{j=1}^i{a[j]}) ,根据中位数的性质,显然邮局建在中位数处是最优的,所以记 (mid=(i+j)/2) ,有:

    [val(i,j)=s[i]-s[mid]-(i-mid)*a[mid]-(s[mid-1]-s[j-1]-(mid-j)*s[mid])\=s[i]+s[j-1]-2*s[mid]-(2*mid-i-j-1)*a[mid] ]

    考虑将转移优化,(val(i,j)) 具有满足四边形不等式的潜质,对于 (val(i,j+1))(val(i+1,j)) ,两者邮局建立的位置相同,设为 (mid) ,可以得到:

    [val(i,j+1)+val(i+1,j)=2*val(i,j+1)-(a[i+1]-a[mid])-(a[mid]-a[j]) ]

    [quadecause val(i,j)leq val(i,j+1)+(a[mid]-a[j]),quad val(i+1,j+1)leq val(i,j+1)+(a[i]-a[mid]) ]

    两式相加可得:

    [val(i,j+1)+val(i+1,j)geq val(i,j)+val(i+1,j+1) ]

    又因为显然 (val(i+1,j)geq val(i,j+1)) ,所以 (dp[i][j]) 满足二维决策单调性,即 (p[i][j-1]leq p[i,j]leq p[i+1][j]) ,其中 (p[i][j])(dp[i][j]) 的决策点 (k) ,这样我们就将时间复杂度降到了 (O(sum_{i=1}^Psum_{j=1}^V(p[i+1][j]-p[i][j-1]))=O(sum_{i=1}^P(p[i+1][V]-p[1][V-i]+V-i))=O(VP)) ,可以轻松通过此题。

    实现细节:

    • 由于转移 (dp[i][j]) 时要用到 (dp[i][j-1])(dp[i+1][j]) 的决策,要采用区间DP的顺序进行循环。
    • 初值:(dp[i][i]=0)(p[i][i]=i)

    Code:

    #include<iostream>
    #include<cstring>
    #define N 3020
    using namespace std;
    int a[N],p[N][N],dp[N][N],s[N];
    int n,m;
    int val(int i,int j){
        int mid=(i+j)>>1;
        return s[i]-s[mid]+(2*mid-i-j)*a[mid]-s[mid-1]+s[j-1];
    }
    bool Min(int &a,int b){
        if(a<=b)return false;
        a=b;return true;
    }
    int main(){
        cin>>n>>m;
        for(int i=1;i<=n;i++)
            cin>>a[i],s[i]=s[i-1]+a[i];
        memset(dp,0x3f,sizeof(dp));
        for(int i=0;i<=n;i++)dp[i][i]=0,p[i][i]=i;
        for(int len=1;len<=n-m;len++){
            for(int i=1,j=1+len;j<=n;i++,j++){
                for(int k=p[i][j-1];k<=p[i+1][j];k++)
                    if(Min(dp[i][j],dp[i-1][k-1]+val(j,k)))p[i][j]=k;
            }
        }
        cout<<dp[m][n];
        return 0;
    }
    
  • 相关阅读:
    72. Edit Distance
    电脑常识
    java try·····catch·····异常处理学习
    java链接sqlserver数据库
    HTTP Status 500
    初识NDA
    Sublime Text_v2.02包含中文包以及使用方法
    ol 与ul 的区别
    word-break: break-all word-break:keep-all word-wrap: break-word三者的区别
    用deamon打开ISO文件,提示命令行错误!!
  • 原文地址:https://www.cnblogs.com/Neal-lee/p/14074009.html
Copyright © 2011-2022 走看看