洛谷P4767 [IOI2000]邮局
题目链接:P4767 [IOI2000]邮局
题目大意:
整数轴上有 \(V\) 个村庄,要在其中一些村庄中建 \(P\) 个邮局,求每个村庄和最近的邮局之间所有距离的总和的最小值。
\(1\leq P\leq 300, P\leq V\leq 3000\)
思路:
记 \(val(i,j)\) 为在 \([j,i]\) 区间上建一个邮局后该区间距离和的最小值,设 \(dp[i][j]\) 为在 \([1,j]\) 上建了 \(i\) 个邮局时的答案,有朴素 \(O(V^2P)\) 转移:
计算 \(val(i,j)\) :,记 \(s[i]\) 为前 \(i\) 个村庄坐标和,即 \(s[i]=\sum_{j=1}^i{a[j]}\) ,根据中位数的性质,显然邮局建在中位数处是最优的,所以记 \(mid=(i+j)/2\) ,有:
考虑将转移优化,\(val(i,j)\) 具有满足四边形不等式的潜质,对于 \(val(i,j+1)\) 和 \(val(i+1,j)\) ,两者邮局建立的位置相同,设为 \(mid\) ,可以得到:
两式相加可得:
又因为显然 \(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}^P\sum_{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;
}