码迷,mamicode.com
首页 > 其他好文 > 详细

斜率优化 DP

时间:2019-11-02 13:54:25      阅读:69      评论:0      收藏:0      [点我收藏+]

标签:href   define   前缀和   ons   getchar   fine   i+1   eof   函数   

CF311B Cats Transport

暑假到现在终于过了这道题??.

首先计算出\(A[]\),\(A[i]\)表示如果有一个饲养员在\(A[i]\)时刻出发,那么刚好使第\(i\)只猫无需等待地被接走.

发现\(A[]\)与猫的编号无关,对其排序并求前缀和\(S[]\).

\(F[i,j]\)表示前\(i\)个饲养员接走了前\(j\)只猫的最小等待时间和.

朴素算法有:

\[F[i,j]=\min_{k<j}(F[i-1,k]+A[j]\times (j-k)-(S[j]-S[i]))\]

分别枚举\(i,j,k\),时间复杂度\(O(n^3)\).

考虑对最内层DP进行斜率优化,此时把与\(i,j\)相关的式子视为常量,把\(k\)视为变量,去掉\(\min\)函数,把状态转移方程变形,有:

\[F[i-1,k]+S[k]=A[j]\times k+F[i,j]-A[j]\times j+S[j] \]

令:

\[y=F[i-1,k]+S[k]\]

\[x=k\]

\[b=F[i,j]-A[j]\times j+S[j]\]

那么有\(y=A[j]\times x+b\)

容易发现这是一个一次函数,一但\(x\)确定,则可以在坐标轴中画出对应的点.换句话说,每一个\(k\)对应的是一个点.这些点的横坐标是单增的,因为\(j\)是从\(1...m\)的.

我们想让\(F[i,j]\)最小,就是让\(b\)最小,也就是让纵截距最小.而斜率\(A[j]\)是确定的显然在所有点中,只有下凸壳上的点可能使纵截距出现最小值.所以我们维护下凸壳.

下凸壳上的哪个点会使纵截距出现最小值呢?如果记\(slope(i,j)\)表示点\(i\)与点\(j\)连线的斜率,那么:

\[slope(i-1,i)<=A[j],slope(i,i+1)>=A[j](*)\]

的点会使纵截距出现最小值.

本题中,\(A[j]\)单增,所以可以用单调队列维护这个下凸壳.队首出队的条件是:队首两点间斜率\(<A[j]\),因为\(A[j]\)单增,而这两点斜率又\(<A[j]\),那么队首的点无论如何不可能成为满足\(*\)式的最优情况.

从队尾入队的条件是:能形成下凸壳.

#include<bits/stdc++.h>
const int SIZE=100005;

int In()
{
    char ch=getchar();
    int x=0;
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x;
}

int n,m,p,h,Tim,sum_D[SIZE];
int A[SIZE];
long long DP[105][SIZE],sum_A[SIZE];
int q[SIZE],L,R;

int main()
{
    scanf("%d%d%d",&n,&m,&p);
    for(int i=2;i<=n;i++)
        sum_D[i]=sum_D[i-1]+In();
    for(int i=1;i<=m;i++)
    {
        h=In();Tim=In();
        A[i]=Tim-sum_D[h];
    }
    std::sort(A+1,A+1+m);
    for(int i=1;i<=m;i++)sum_A[i]=sum_A[i-1]+A[i];
    memset(DP,0x3F,sizeof(DP));
    DP[0][0]=0;
    for(int i=1;i<=p;i++)
    {
        L=R=1;
        q[1]=0;
        #define G(x) (DP[i-1][x]+sum_A[x])
        #define S(A,B) (double)(G(B)-G(A))/(double)(B-A)
        for(int k=1;k<=m;k++)
        {
            while( L<R && S(q[L],q[L+1]) <= 1.0*A[k] )L++;
            DP[i][k]=DP[i-1][q[L]]+1LL*A[k]*(k-q[L])-(sum_A[k]-sum_A[q[L]]);
            while( L<R && S(q[R-1],q[R]) >= S(q[R],k) )R--;
            q[++R]=k;
        }
    }
    std::cout<<DP[p][m];
    return 0;
}

斜率优化 DP

标签:href   define   前缀和   ons   getchar   fine   i+1   eof   函数   

原文地址:https://www.cnblogs.com/TaylorSwift13/p/11781248.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!