正题
P4767
题目大意
给出坐标轴上的n个点,让你选择m个点作为特殊点,使所有点到最近特殊点的距离之和最小
解题思路
考虑对于一个区间选择一个特殊点的最小代价,可以把所有点到当前点的路径分割开来,即每段距离走的次数为1,2,3…l-1,l,r,r-1…3,2,1,其中连接l,r的就是选择的特殊点,显然让l,r相同代价最小,那么取中间点即可
在构造时可以看作是构造等腰三角形(高度为走的次数),那么每次往mid-r中加一次即可
然后考虑DP
设 fi,jf_{i,j}fi,j 为前 i 个点中选择 j 个特殊点的最小贡献,那么没到一个状态考虑从哪个点开始重新构建一个新区间(这个区间的最短距离都是到新的一个特殊点),那么时间复杂度 O(n2m)O(n^2m)O(n2m)
考虑用四边形不等式优化(笔者写的不好,若看不懂可以到洛谷看题解qaq)
设 dis 为区间最小代价,sum 为路径长度
令 mid=b+a+12mid=\frac{b+a+1}{2}mid=2b+a+1
若 2∣(b−(a+1))2|(b-(a+1))2∣(b−(a+1))
disa,b+disa+1,b+1=disa,b+disa+1,b+1=suma,mid+disa+1,b+disa+1,b+summid,b+1=disa+1,b+suma,b+1+disa+1,b=disa,b+1+disa+1,b\begin{aligned}dis_{a,b}+dis_{a+1,b+1}&=dis_{a,b}+dis_{a+1,b+1}\\ &=sum_{a,mid}+dis_{a+1,b}+dis_{a+1,b}+sum_{mid,b+1}\\ &=dis_{a+1,b}+sum_{a,b+1}+dis_{a+1,b}\\ &=dis_{a,b+1}+dis_{a+1,b}\end{aligned}disa,b+disa+1,b+1=disa,b+disa+1,b+1=suma,mid+disa+1,b+disa+1,b+summid,b+1=disa+1,b+suma,b+1+disa+1,b=disa,b+1+disa+1,b
若 2∤(b−(a+1))2\nmid(b-(a+1))2∤(b−(a+1))
disa,b+disa+1,b+1=disa,b+disa+1,b+1=suma,mid+disa+1,b+disa+1,b+summid+1,b+1≥disa+1,b+suma,b+1+disa+1,b=disa,b+1+disa+1,b\begin{aligned}dis_{a,b}+dis_{a+1,b+1}&=dis_{a,b}+dis_{a+1,b+1}\\ &=sum_{a,mid}+dis_{a+1,b}+dis_{a+1,b}+sum_{mid+1,b+1}\\ &\geq dis_{a+1,b}+sum_{a,b+1}+dis_{a+1,b}\\ &=dis_{a,b+1}+dis_{a+1,b}\end{aligned}disa,b+disa+1,b+1=disa,b+disa+1,b+1=suma,mid+disa+1,b+disa+1,b+summid+1,b+1≥disa+1,b+suma,b+1+disa+1,b=disa,b+1+disa+1,b
综上,该转移满足四边形不等式
然后可以用二维的决策单调性优化转移
时间复杂度 O(nm)O(nm)O(nm)
code
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define N 3010
using namespace std;
int n,m,a[N],f[N][N],d[N][N],dis[N][N];
int main()
{scanf("%d%d",&n,&m);for(int i=1;i<=n;++i)scanf("%d",&a[i]);for(int i=1;i<=n;++i){dis[i][i]=0;for(int j=i+1;j<=n;++j)dis[i][j]=dis[i][j-1]+a[j]-a[i+j>>1];}memset(f,127/3,sizeof(f));f[0][0]=0;for(int j=1;j<=m;++j){d[n+1][j]=n;for(int i=n;i>0;--i)for(int k=d[i][j-1];k<=d[i+1][j];++k)if(f[k][j-1]+dis[k+1][i]<f[i][j])f[i][j]=f[k][j-1]+dis[k+1][i],d[i][j]=k;}printf("%d",f[n][m]);return 0;
}