【斜率优化】运输小猫
-----思路------
一个小贪心:
设\(a_i=t_i-\sum_{j=1}^{h_i}d_j\),那么饲养员就必须在a_i时刻之后从1号山出发
这只猫的等待时间就是\(t-a_i\)
显然按照\(a_i\)从小到大排序后,每个饲养员一次都应该带走连续的几只
设\(f[i][j]\)表示前i个饲养员带走前j只猫咪的最小等待时间
状态转移方程为:
\(f[i][j]=min_{0<=k<j}\{f[i-1][k]+a_j*(j-k)-(s_j-s_k)\}\)
枚举\(k\),求出\(f[i][j]\),复杂度为\(O(p*m^2)\)
将外层循环\(i\)看成定值,将j看成状态变量,k看成决策变量
将\(min\)函数拆开得
\(f[i-1][k]+s_k=a_j*k+f[i][j]-a_j*j+s_j\)
把k作为横坐标,\(f[i-1][k]+s_k\)作为纵坐标,那么截距最小时,\(f[i][j]\)最小
可以用单调队列来维护下凸壳
-----debug-------
1.二位数组开了个1e5*1e5的,ce了
2.一堆()写的乱了,很迷
3.忘记在第一层for循环的时候把队列的指针重置一下了
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read() {
char c = getchar();
int x = 0, f = 1;
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
const int N=1e5+2020;
int n,m,p;
int q[N];
int f[110][N],sumd[N],suma[N],a[N];
signed main()
{
n=read(),m=read(),p=read();
for(int i=2;i<=n;++i)
{
int high=read();
sumd[i]=sumd[i-1]+high;
}
for(int i=1;i<=m;++i)
{
int h=read(),t=read();
a[i]=t-sumd[h];
}
sort(a+1,a+1+m);
int l=0,r=0;
for(int i=1;i<=m;++i) suma[i]=suma[i-1]+a[i];
memset(f,0x3f,sizeof(f));
f[0][0]=0;
for(int i=1;i<=p;++i)
{
q[1]=0;
l=1;r=1;
for(int j=1;j<=m;++j)
{
while(l<r&&f[i-1][q[l+1]]+suma[q[l+1]]-f[i-1][q[l]]
-suma[q[l]]<=a[j]*(q[l+1]-q[l])) l++;
f[i][j]=f[i-1][q[l]]+suma[q[l]]-q[l]*a[j]+j*a[j]-suma[j];
while(l<r&&(f[i-1][q[r]]+suma[q[r]]-f[i-1][q[r-1]]-
suma[q[r-1]])*(j-q[r])>(f[i-1][j]+suma[j]-f[i-1][q[r]]-suma[q[r]])*(q[r]-q[r-1])) r--;
q[++r]=j;
}
}
cout<<f[p][m];
return 0;
}
$$Life \quad is \quad fantastic!$$