• 斜率优化入门题:任务安排$123$ : )


    任务安排1

    1≤N≤5000,1≤S≤50,1≤Ti,Ci≤100

    朴素做法:

    $f[i][j]=min {f[k][j-1]+(S*j+sumT)*(sumC[i]-sumC[k])}$

    复杂度为$O(N^{3})$

    优化:

    "费用提前计算思想"

    发现每重新启动一次,由于启动而增加的总费用是可以直接计算的,为$sumC[n]-sumC[k]$

    $f[i]=min f[j]+sumT[i]*(sumC[i]-sumC[j])+S*(sumC[n]-sumC[j]) $

    所以可以去掉j的一维,复杂度降为$O(N^{2})$

    #include<iostream>
    #include<cstdio>
    #define Rg register
    #define il inline
    #define go(i,a,b) for(Rg int i=a;i<=b;i++)
    #define yes(i,a,b) for(Rg int i=a;i>=b;i--)
    #define inf 2100000000
    using namespace std;
    int n,s,t[5010],c[5010],f[5010];
    il int read()
    {
        int x=0,y=1;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
        return x*y;
    }
    int main()
    {
        n=read(),s=read();
        go(i,1,n)t[i]=t[i-1]+read(),c[i]=c[i-1]+read(),f[i]=inf;
        go(i,1,n)//i+1~j
            go(j,0,i-1)
            f[i]=min(f[i],f[j]+t[i]*(c[i]-c[j])+s*(c[n]-c[j]));
        printf("%d
    ",f[n]);
        return 0;
    }
    View Code

    任务安排2

    1≤N≤3*105,1≤S,Ti,Ci≤512

    进一步优化

    把$min$函数去掉,$f[j]$,$sumC[j]$看做变量

    $f[i]=f[j]-(sumT[i]+S)*sumC[j]+sumT[i]*sumC[i]+S*sumC[n]$

    $f[j]=(sumT[i]+S)*sumC[j]-sumT[i]*sumC[i]-S*sumC[n]+f[i]$

    在以$sumC[j]$为横坐标,$f[j]$为纵坐标的平面直角坐标系中,它是一条以$sumT[i]+S$为斜率,$(f[i]-sumT[i]*sumC[i]-S*sumC[n])$为截距的直线,每个决策$j$都对应着坐标系中的一个点,由于 $(-sumT[i]*sumC[i]-S*sumC[n])$ 的值一定,所以当直线截距最小时,$f[i]$取得最小

    于是我们只要维护一个相邻两点线段斜率单调递增的下凸壳即可.答案就是比$sumT[i]+S$大的最小斜率所对应的截距,另外,由于$sumT[i]+S$也是递增的,所以对于每一个$i$斜率比$sumT[i]+S$小的可以直接弹出队列,因为它以后也不会成为答案

    $Attention!$单调队列中的第一个元素不是第一个$j$对应的点,而是$(0,0)!$

    #include<iostream>
    #include<cstdio>
    #define il inline
    #define Rg register
    #define go(i,a,b) for(Rg int i=a;i<=b;i++)
    #define yes(i,a,b) for(Rg int i=a;i>=b;i++)
    #define inf 2100000000
    using namespace std;
    il int read()
    {
        int x=0,y=1;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
        return x*y;
    }
    const int N=300010;
    int n,s,c[N],t[N],f[N],q[N];
    int main()
    {
        n=read(),s=read();
        go(i,1,n)t[i]=t[i-1]+read(),c[i]=c[i-1]+read();
        int l=1,r=1;//q[1]=(0,0)!!
        go(i,1,n)
        {
            while(l<r && (f[q[l+1]]-f[q[l]])<=(s+t[i])*(c[q[l+1]]-c[q[l]]))l++;
            f[i]=f[q[l]]-(s+t[i])*c[q[l]]+t[i]*c[i]+s*c[n];
            while(l<r && (f[q[r]]-f[q[r-1]])*(c[i]-c[q[r]])>=(f[i]-f[q[r]])*(c[q[r]]-c[q[r-1]]))r--;
            q[++r]=i;
            
        }
        printf("%d
    ",f[n]);
        return 0;
    }
    View Code

    任务安排3

    1≤N≤3*105,0≤S,Ci≤512,-512≤Ti≤512

    $T$有可能为负,所以直线的斜率$sumT[i]+S$不是单调递增的,所以当前答案左边的点以后也有可能成为答案,也就是说我们要维护整个下凸壳.这样显然队首不一定是最优答案,要在队列中二分查找一个点$p$,使得它左边的线段斜率小于等于$sumT[i]+S$,右边的线段斜率大于$sumT[i]+S$. $p$就是最优解.

    #include<iostream>
    #include<cstdio>
    #define il inline
    #define Rg register
    #define go(i,a,b) for(Rg int i=a;i<=b;i++)
    #define yes(i,a,b) for(Rg int i=a;i>=b;i++)
    #define inf 2100000000
    #define int long long
    using namespace std;
    il int read()
    {
        int x=0,y=1;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
        return x*y;
    }
    const int N=300010;
    int n,s,c[N],t[N],q[N],f[N];
    il int find(int l,int r,int x)
    {
        int ret;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if((f[q[mid]]-f[q[mid-1]])<=1LL*x*(c[q[mid]]-c[q[mid-1]]))ret=mid,l=mid+1;
            else r=mid-1;
        }
        return q[ret];
    }
    main()
    {
        n=read(),s=read();
        go(i,1,n)t[i]=t[i-1]+read(),c[i]=c[i-1]+read();
        int l=1,r=1;//q[1]=(0,0)!!
        go(i,1,n)
        {
            //while(l<r && (f[q[l+1]]-f[q[l]])<=1LL*(s+t[i])*(c[q[l+1]]-c[q[l]]))l++;
            int p=find(l,r,s+t[i]);
            f[i]=f[p]-1LL*(s+t[i])*c[p]+1LL*t[i]*c[i]+1LL*s*c[n];
            while(l<r && 1LL*(f[q[r]]-f[q[r-1]])*(c[i]-c[q[r]])>=1LL*(f[i]-f[q[r]])*(c[q[r]]-c[q[r-1]]))r--;
            q[++r]=i;
            
        }
        printf("%lld
    ",f[n]);
        return 0;
    }
    View Code
    光伴随的阴影
  • 相关阅读:
    Pycharm2017应用小技巧
    浅谈哈希表
    攻克网页文字不可复制的难题
    Java中List的相关知识
    电脑实用小技巧
    Jme3涉及的eclipse知识
    Word2010撤销按钮失效,Ctrl+Z失效解决办法
    Word文档中怎么删除空白页?删除空白页的六种方法
    word中分栏后文字均匀的分布在了左右两栏,而不是填满左栏再填右栏,怎么办?
    visdom服务启动时提示Downloading scripts, this may take a little while解决办法
  • 原文地址:https://www.cnblogs.com/forward777/p/11250841.html
Copyright © 2020-2023  润新知