传送门
描述
小 S 是农场主,他养了 M 只猫,雇了 P 位饲养员。农场中有一条笔直的路,路边有 N 座山,从 1 到 N 编号。第 i 座山与第 i−1 座山之间的距离是 Di 。饲养员都住在 1 号山上。
有一天,猫出去玩。第 i只猫去 Hi 号山玩,玩到时刻 Ti 停止,然后在原地等饲养员来接。饲养员们必须回收所有的猫。每个饲养员沿着路从 1 号山走到 N 号山,把各座山上已经在等待的猫全部接走。饲养员在路上行走需要时间,速度为 1 米每单位时间。饲养员在每座山上接猫的时间可以忽略,可以携带的猫的数量为无穷大。
例如有两座相距为 1 的山,一只猫在 2 号山玩,玩到时刻 3 开始等待。如果饲养员从 1 号山在时刻 2 或 3 出发,那么他可以接到猫,猫的等待时间为 0 或 1 。而如果他于时刻 1 出发,那么他将于时刻 2 经过 2 号山,不能接到当时仍在玩的猫。
你的任务是规划每个饲养员从 1 号山出发的时间,使得所有猫等待时间的总和尽量小。饲养员出发的时间可以为负。
输入
第一行三个整数 N,M,P
第二行 N−1 个正整数 Di ,表示第 i 座山与第 i−1座山之间的距离是 Di
接下去 M行每行两个整数 Hi,Ti
输出
输出一个整数表示答案。
样例输入 [复制]
4 6 2
1 3 5
1 0
2 1
4 9
1 10
2 10
3 12
样例输出 [复制]
3
数据范围:
对于全部数据,2≤N≤1e5,1≤M≤1e5,1≤p≤100,1≤Di<1e4,1≤Hi≤N,0≤Ti≤1e9
SOL
简化模型很重要。。。
模拟 样例,发现对于 一部分由一个人一次接的喵,相当于分了一个组。。
我们即需要知道如何 “分组”
约束很多,我们可以尝试 “消元”;
对于第 i 只喵, 只要出发时间 t >= Ti- (H[1]+…+H[i]) 就可以被接到。
显然,选择一个很晚的时间出发可以接到所有喵
令 a [i] = Ti- (H[1]+…+H[i])
等待时间(代价) : t- a[i]
这时有一个显然的贪心:
将 a 升序排序 , 选择相邻的 a ,出发时间 :t= a(max)
这肯定是 局部最优的
问题变成了 有一个升序的序列 a, 分成 p 个区间 ,每个区间的代价是 区间每一个数和区间最大值的差值的绝对值之和 使总代价最小
dp方程就显然了 :(s[i]是a数组前缀和
dp[i][k]=min(dp[j][k-1] +(i-j)*a[i]-(s[i]-s[j]))
之后是一个基本单调队列操作。
个人小结:
1.斜率比较的时候,如果交叉相乘要注意变号。。。。
2.队列里维护的不一定是当前维度的答案。本题就是维护上一维度的答案。本质:在备选决策中优化选择时间 。对象:备选决策。
3.注意0在本题的状态定义下是有实际含义的,队列里面一开始初始化是有0这样一个备选决策 (前缀和 s[i]-s[j] 0<=j<=i-1)
dp[0][0]=0,而dp[k][0]不存在,初始化成 INF
4.哪位大佬比较一下 斜率用交叉相乘好一些还是直接除 (感觉直接除更直观,不容易写挂。。)
SOL
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=1e5+10;
int dp[maxn][150],s[maxn],a[maxn];
int h[maxn];
int n,m,P;
int q[maxn];
inline int Y(int i,int k){
return dp[i][k-1]+s[i];
}
signed main(){
scanf("%d%d%d",&n,&m,&P);
for(int i=2;i<=n;++i)scanf("%d",&h[i]),h[i]+=h[i-1];
for(int i=1;i<=m;++i){
int x,y;scanf("%d%d",&x,&y);
a[i]=y-h[x];
}
sort(a+1,a+m+1);
for(int i=1;i<=m;++i)s[i]=s[i-1]+a[i];
memset(dp,0x5f,sizeof dp);
dp[0][0]=0;
for(int k=1;k<=P;++k){
int head=1,tail=1;
for(int i=1;i<=m;++i){
while(head<tail&&(Y(q[head+1],k)-Y(q[head],k))<=(q[head+1]-q[head])*a[i])++head;
int j=q[head];
dp[i][k]=dp[j][k-1]+(i-j)*a[i]-s[i]+s[j];
// cerr<<j<<endl;
// cerr<<i<<" "<<k<<" "<<dp[i][k]<<endl;
while(head<tail&&(Y(q[tail-1],k)-Y(q[tail],k))*(q[tail-1]-i)>=(Y(q[tail-1],k)-Y(i,k))*(q[tail-1]-q[tail]))--tail;
q[++tail]=i;
}
}
printf("%lld",dp[m][P]);
return 0;
}