poj 2726: [SDOI2012]任务安排【斜率优化dp】

我会斜率优化了!这篇讲的超级棒https://blog.csdn.net/shiyongyang/article/details/78299894?readlog
首先列个n方递推,设sf是f的前缀和,st是t的前缀和:\( f[i]=min(f[j]+s(sf[n]-sf[j])+st[i](sf[i]-sf[j])) \) 然后移项:
\[ f[i]=f[j]+s*sf[n]-s*sf[j]+st[i]*sf[i]-st[i]*sf[j] \]
\[ f[i]=f[j]+s*sf[n]+st[i]*sf[i]-s*sf[j]-st[i]*sf[j] \]
\[ f[i]=f[j]+s*sf[n]+st[i]*sf[i]-sf[j]*(s+st[i]) \]
\[ f[i]+sf[j]*(s+st[i])=f[j]+s*sf[n]+st[i]*sf[i] \]
然后看成斜率表达式b+kx=y,那么b=f[i],x=sf[j],k=(s+st[i]),y=f[j]+ssf[n]+st[i]sf[i]
那么对于每个i都看成一个点(sf[i],f[j]+ssf[n]+st[i]sf[i]),然后在队列里维护斜率单调递增的点(下凸壳),考虑对于当前的i的斜率k,需要在下凸壳上找一个j点使得f[i],也就是b(截距)最小。
显然要找最后一条比k斜率小的点的右端点
然后用斜率去卡队首即可,加点的时候用斜率判断是否是下凸壳,否则去队尾

#include<iostream>
#include<cstdio>
using namespace std;
const long long N=10005,inf=1e18;
long long n,s,st[N],sf[N],f[N],q[N],l,r;
long long read()
{
    long long r=0,f=1;
    char p=getchar();
    while(p>'9'||p<'0')
    {
        if(p=='-')
            f=-1;
        p=getchar();
    }
    while(p>='0'&&p<='9')
    {
        r=r*10+p-48;
        p=getchar();
    }
    return r*f;
}
double wk(int j,int k)
{
    return (double)((double)f[j]-(double)f[k])/(double)((double)sf[j]-(double)sf[k]);
}
int main()
{
    n=read(),s=read();
    for(int i=1;i<=n;i++)
        st[i]=st[i-1]+read(),sf[i]=sf[i-1]+read();
    // for(int i=1;i<=n;i++)
        // cerr<<st[i]<<" "<<sf[i]<<endl;
    for(int i=1;i<=n;i++)
    {//cerr<<wk(q[l+1],q[l])<<"   "<<s+st[i]<<endl;
        while(l<r&&wk(q[l+1],q[l])<s+st[i])
            l++;
        f[i]=f[q[l]]+s*(sf[n]-sf[q[l]])+st[i]*(sf[i]-sf[q[l]]);//cerr<<q[l]<<" "<<f[i]<<endl;
        // for(int j=l;j<=r;j++)
            // cerr<<q[j]<<" ";
        // cerr<<endl;
        while(l<r&&wk(i,q[r])<wk(q[r],q[r-1]))
            r--;
        q[++r]=i;
    }
    printf("%lld\n",f[n]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/lokiii/p/9092469.html