洛谷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;
}
原文地址:https://www.cnblogs.com/Neal-lee/p/14074009.html