题意:给定 M M M个班车,每个班车 p i p_i pi时刻从 x i x_i xi发车 q i q_i qi到达 y i y_i yi,等车 t t t时间花费代价 A t 2 + B t + C At^2+Bt+C At2+Bt+C,在 t t t时刻到达花费 t t t的代价,求从 1 1 1到 N N N的最小花费。
1 ≤ N ≤ 100000 , 1 ≤ M ≤ 200000 1 \leq N \leq 100000,1 \leq M \leq 200000 1≤N≤100000,1≤M≤200000
显然是个dp
容易想到一个二维dp,第一维记当前位置,第二维记时间
由于数据范围很大,所以需要优化掉一维
仔细想想为什么需要两维?
因为位置是有后效性的,需要用时间节点来标记。
而时间是一去不复返的,是个天然的无后效性状态。
所以可以考虑按时间dp,先假设没有位置限制。
此题对时间唯一的限制是不能冲突,所以按照班车的时间段dp。
假设可以瞬间移动,不计最终代价,设坐上第 i i i辆班车的最小代价是 f i f_i fi
f i = min q j ≤ p i { f j + A ( p i − q j ) 2 + B ( p i − q j ) + C } f_i=\min_{q_j \leq p_i}\{f_j+A(p_i-q_j)^2+B(p_i-q_j)+C\} fi=qj≤pimin{fj+A(pi−qj)2+B(pi−qj)+C}
盲猜斜率优化
假设决策 j , k j,k j,k有 j < k j<k j<k, k k k比 j j j优
即
f k + A ( p i − q j ) 2 + B ( p i − q j ) + C ≤ f j + A ( p i − q j ) 2 + B ( p i − q j ) + C f_k+A(p_i-q_j)^2+B(p_i-q_j)+C\leq f_j+A(p_i-q_j)^2+B(p_i-q_j)+C fk+A(pi−qj)2+B(pi−qj)+C≤fj+A(pi−qj)2+B(pi−qj)+C
f k + A ( p i − q j ) 2 + B ( p i − q j ) ≤ f j + A ( p i − q j ) 2 + B ( p i − q j ) f_k+A(p_i-q_j)^2+B(p_i-q_j)\leq f_j+A(p_i-q_j)^2+B(p_i-q_j) fk+A(pi−qj)2+B(pi−qj)≤fj+A(pi−qj)2+B(pi−qj)
f k + A p i 2 − 2 A p i q k + q k 2 + B p i − B q k ≤ f j + A p i 2 − 2 A p i q j + q j 2 + B p i − B q j f_k+Ap_i^2-2Ap_iq_k+q_k^2+Bp_i-Bq_k \leq f_j+Ap_i^2-2Ap_iq_j+q_j^2+Bp_i-Bq_j fk+Api2−2Apiqk+qk2+Bpi−Bqk≤fj+Api2−2Apiqj+qj2+Bpi−Bqj
f k − 2 A p i q k + q k 2 − B q k ≤ f j − 2 A p i q j + q j 2 − B q j f_k-2Ap_iq_k+q_k^2-Bq_k \leq f_j-2Ap_iq_j+q_j^2-Bq_j fk−2Apiqk+qk2−Bqk≤fj−2Apiqj+qj2−Bqj
2 A p i + B ≥ f k + q k 2 − f j − q j 2 q k − q j 2Ap_i+B \geq\frac{f_k+q_k^2-f_j-q_j^2}{q_k-q_j} 2Api+B≥qk−qjfk+qk2−fj−qj2
发现 p p p和 q q q分开了(其实不分开也能做的说)
所以可以把出发和到达拆开,分别排序,双指针维护
考虑位置限制
对于一个 p i p_i pi,只有 y j = x i y_j=x_i yj=xi的 q j q_j qj会更新答案
所以可以用vector存 N N N个凸壳
遇到出发在对应结点的凸壳上算出 f f f。因为脑袋抽了写了个二分。
遇到到达在对应结点用之前的 f f f更新凸壳
最后能到达 N N N的算答案。
当时做的时候式子推反了,然后二分的时候凸壳算反了就负负得正A了……
我可能是个傻子
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <vector>
#define MAXN 200005
using namespace std;
struct event{int idx,pos,tim;}st[MAXN],ed[MAXN];
inline bool cmp(const event& a,const event& b){return a.tim<b.tim;}
int n,m,A,B,C;
int f[MAXN];
inline int Y(const int& i){return f[ed[i].idx]+ed[i].tim*ed[i].tim;}
inline int calc(const int& i,const int& j)
{
int d=st[i].tim-ed[j].tim;
return f[ed[j].idx]+A*d*d+B*d+C;
}
vector<int> v[MAXN];
int vis[MAXN];
int main()
{
scanf("%d%d%d%d%d",&n,&m,&A,&B,&C);
for (int i=1;i<=m;i++)
{
int x,y,p,q;
scanf("%d%d%d%d",&x,&y,&p,&q);
st[i]=(event){i,x,p};
ed[i]=(event){i,y,q};
}
memset(f,0x7f,sizeof(f));
sort(st+1,st+m+1,cmp);
sort(ed+1,ed+m+1,cmp);
int now=0;
for (int i=1;i<=m;i++)
{
while (now<m&&ed[now+1].tim<=st[i].tim)
{
++now;
if (vis[ed[now].idx]) continue;
vector<int>& cur=v[ed[now].pos];
while (cur.size()>1&&(Y(now)-Y(cur[cur.size()-1]))*(ed[cur[cur.size()-1]].tim-ed[cur[cur.size()-2]].tim)<=((Y(cur[cur.size()-1])-Y(cur[cur.size()-2]))*(ed[now].tim-ed[cur[cur.size()-1]].tim)))
cur.pop_back();
cur.push_back(now);
}
if (st[i].pos==1) f[st[i].idx]=A*st[i].tim*st[i].tim+B*st[i].tim+C;
else
{
vector<int>& cur=v[st[i].pos];
if (cur.size())
{
int l=0,r=cur.size()-1,mid;
while (l<r)
{
mid=(l+r)>>1;
if ((2*A*st[i].tim+B)*(ed[cur[mid+1]].tim-ed[cur[mid]].tim)<=Y(cur[mid+1])-Y(cur[mid])) r=mid;
else l=mid+1;
}
f[st[i].idx]=calc(i,cur[l]);
}
else vis[st[i].idx]=true;
}
}
int ans=f[0];
for (int i=1;i<=m;i++) if (ed[i].pos==n) ans=min(ans,f[ed[i].idx]+ed[i].tim);
printf("%d\n",ans);
return 0;
}
本文详细解析了一种基于动态规划的班车调度算法,通过优化时间维度的状态转移方程,实现从起点到终点的最小花费路径计算。算法巧妙利用了斜率优化技巧,将决策过程简化为基于时间区间的DP,并通过维护凸壳结构来处理位置限制,适用于大规模数据集。
488

被折叠的 条评论
为什么被折叠?



