时间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
在一张2D地图上有N座城市,坐标依次是(X1, Y1), (X2, Y2), … (XN, YN)。
现在H国要修建K条平行于X轴的天然气主管道。这些管道非常长,可以认为是一条条平行于X轴的直线。
小Ho想知道如何修建这K条管道,可以使N座城市到最近的管道的垂直距离之和最小。请你求出这个最小的距离之和。
输入
第一行包含2个整数N和K。
以下N行每行包含两个整数Xi, Yi。
1 <= N <= 10000
1 <= K <= 1000
0 <= Xi, Yi <= 1000000
输出
一个整数,代表最小的距离之和。
样例输入
4 2
0 0
0 100
100 0
100 100
样例输出
0
思路:可以想到将管道修在城市所在y坐标是满足答案的。
将城市按y坐标从小到大排序, s u m [ i ] sum[i] sum[i]代表前 i i i个城市的y坐标之和, d [ i ] [ j ] d[i][j] d[i][j]表示前 i i i个城市共修建了 j j j个管道且第 i i i个城市处修建了一个管道的最小距离; f [ i ] [ j ] f[i][j] f[i][j]表示前 i i i个城市共修建了 j j j个管道(第 i i i个城市处可能没有修建管道)的最小距离。
则转移方程:
d
[
i
]
[
j
]
=
f
[
k
]
[
j
−
1
]
+
(
i
−
k
)
∗
y
[
i
]
−
(
s
u
m
[
i
]
−
s
u
m
[
k
]
)
d[i][j]=f[k][j-1]+(i-k)*y[i]-(sum[i]-sum[k])
d[i][j]=f[k][j−1]+(i−k)∗y[i]−(sum[i]−sum[k]) (
[
k
+
1
,
i
]
之
间
的
城
市
选
择
i
作
为
最
近
的
管
道
[k+1,i]之间的城市选择i作为最近的管道
[k+1,i]之间的城市选择i作为最近的管道)
f [ i ] [ j ] = d [ k ] [ j ] + s u m [ i ] − s u m [ k ] − ( i − k ) ∗ y [ k ] f[i][j]=d[k][j]+sum[i]-sum[k]-(i-k)*y[k] f[i][j]=d[k][j]+sum[i]−sum[k]−(i−k)∗y[k] ( [ k + 1 , i ] 之 间 的 城 市 选 择 k 作 为 最 近 的 管 道 [k+1,i]之间的城市选择k作为最近的管道 [k+1,i]之间的城市选择k作为最近的管道)
转化后即为: f [ k ] [ j − 1 ] + s u m [ k ] = k ∗ y [ i ] + s u m [ i ] − i ∗ y [ i ] + d [ i ] [ j ] f[k][j-1]+sum[k]=k*y[i]+sum[i]-i*y[i]+d[i][j] f[k][j−1]+sum[k]=k∗y[i]+sum[i]−i∗y[i]+d[i][j] d [ k ] [ j ] − s u m [ k ] + k ∗ y [ k ] = i ∗ y [ k ] − s u m [ i ] + f [ i ] [ j ] d[k][j]-sum[k]+k*y[k]=i*y[k]-sum[i]+f[i][j] d[k][j]−sum[k]+k∗y[k]=i∗y[k]−sum[i]+f[i][j]
这样就可以用斜率优化来进行DP转移了。
#include<bits/stdc++.h>
using namespace std;
const int MOD=1e9+7;
const int MAX=1e4+10;
typedef long long ll;
ll sum[MAX];
struct Point
{
ll x,y;
}p[MAX];
int cmp(const Point&A,const Point &B){return A.y<B.y;}
ll d[MAX][1010];
ll f[MAX][1010];
ll X[MAX],Y[MAX];
int main()
{
int n,m;
cin>>n>>m;
for(ll i=1;i<=n;i++)scanf("%lld%lld",&p[i].x,&p[i].y);
sort(p+1,p+n+1,cmp);
for(ll i=1;i<=n;i++)sum[i]=sum[i-1]+p[i].y;
for(ll i=1;i<=n;i++)d[i][1]=p[i].y*i-sum[i];
ll L,R;
for(ll j=1;j<=m;j++)
{
L=0,R=0;
for(ll i=1;i<=n;i++)
{
while(L<R&&Y[L+1]-X[L+1]*i<=Y[L]-X[L]*i)L++;
f[i][j]=min(d[i][j],Y[L]-i*X[L]+sum[i]);
ll y=d[i][j]-sum[i]+i*p[i].y;
ll x=p[i].y;
while(L<R&&(Y[R]-Y[R-1])*(x-X[R-1])>=(y-Y[R-1])*(X[R]-X[R-1]))R--;
X[R+1]=x;
Y[R+1]=y;
R++;
}
L=0,R=0;
for(ll i=1;i<=n;i++)
{
while(L<R&&Y[L+1]-X[L+1]*p[i].y<=Y[L]-X[L]*p[i].y)L++;
d[i][j+1]=Y[L]-p[i].y*X[L]-sum[i]+p[i].y*i;
ll y=f[i][j]+sum[i];
ll x=i;
while(L<R&&(Y[R]-Y[R-1])*(x-X[R-1])>=(y-Y[R-1])*(X[R]-X[R-1]))R--;
X[R+1]=x;
Y[R+1]=y;
R++;
}
}
cout<<f[n][m]<<endl;
return 0;
}