[2017纪中10-23]公交运输 DP+斜率(玄学)优化

30 篇文章 0 订阅
3 篇文章 0 订阅

题目链接:https://jzoj.net/senior/#main/show/5415
首先可以推出n^2的DP方程:F[i]=min{F[j]+(i-j)/C[i]*V[i]} (C[i]|(i-j))。
我们发现靠前的 dp 值对之后 dp 值的贡献是一个形如一条直线(一次函数)的形式。这样我们在计算后面的 dp 值时,就是找到之前所有可能转移到这个点的所有直线在当前横坐标的最小值。我们发现 C [i]和 i%C[i] 相同的两点可以转移到之后车站的集合是相同的。所以可以维护 maxc*maxc 个栈来维护。

转移方程简化为 F[i]=min{F[j]+dis*V[i]},dis为i到j要坐几站。

问题转化为维护栈里的下凸壳,每次询问一个横坐标(满足询问递增),求与下凸壳交点的纵坐标(与直线集交点的最小值)。

栈里的直线是随着i递增而加入的,首先要保证斜率(V[i])递增。
若存在x < y,V[x] > V[y],一直乘坐x车不如到y之后换乘y车。

接着就是要满足下凸壳性质,新加一条直线时假如会把栈顶的直线从凸壳中去除,那么就要退栈。

算答案的时候,假如栈顶的答案大于栈顶-1的答案,那么栈顶的直线再也不会成为最有决策(因为斜率递增),所以也要退栈,知道小于才更新答案。

WA点:求直线交点写成整除,直线方程几次没写对。
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1000010;
int n,maxc,f[maxn],c[maxn],v[maxn],st[11][10][20100],top[11][10];
int G(int x,int y){return f[y]+(x-y)/c[y]*v[y];}
double Q(int x,int y){return (v[y]==v[x])?1e18:(double)(f[x]-f[y]-v[x]*(x/c[x])+v[y]*(y/c[y]))/(v[y]-v[x]);} 
int main()
{
    scanf("%d%d",&n,&maxc);
    for(int i=0;i<n;i++)
        scanf("%d%d",&c[i],&v[i]);
    memset(top,0,sizeof(top));
    memset(f,0x3f,sizeof(f));
    f[0]=0;
    for(int i=0;i<n;i++)
    {
        int k=c[i],l=i%c[i];
        while(top[k][l]>0&&(v[i]<=v[st[k][l][top[k][l]]])) {top[k][l]--;}
        while(top[k][l]>1&&Q(i,st[k][l][top[k][l]-1])>=Q(st[k][l][top[k][l]],st[k][l][top[k][l]-1])) {top[k][l]--;}
        st[c[i]][i%c[i]][++top[c[i]][i%c[i]]]=i;
        for(int d=1;d<=maxc;d++) 
        {
            l=(i+1)%d;
            while(top[d][l]>1&&G(i+1,st[d][l][top[d][l]])>=G(i+1,st[d][l][top[d][l]-1])) top[d][l]--;
            if(top[d][l]>0) f[i+1]=min(f[i+1],G(i+1,st[d][l][top[d][l]]));
        }
    }
    for(int i=1;i<=n;i++)
        if(f[i]>1e9) printf("-1 ");
        else printf("%d ",f[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值