斜率优化 学习笔记

斜率优化 学习笔记

https://www.cnblogs.com/Xing-Ling/p/11210179.html

总结:

移项将 Dp 方程化为 \(y=kx+b\) 的形式,其中,\(x,\ y\) 仅与 \(j\) 有关,\(k,\ b\) 仅与 \(i\) 有关。

\(y(dp[j]+A)=k(?_i)x(B\times ?_j)+b(dp[i]+C)\) 的形式。

https://oi-wiki.org/dp/opt/slope/#%E4%BE%8B%E9%A2%98%E5%BC%95%E5%85%A5

考虑一次函数的斜截式 \(y=kx+b\),将其移项得到 \(b=y-kx\)。我们将与 \(j\) 有关的信息表示为 \(y\) 的形式,把同时与 \(i,j\) 有关的信息表示为 \(kx\),把要最小化的信息(与 \(i\) 有关的信息)表示为 \(b\),也就是截距。具体地,设

\[\begin{aligned} x_j&=s_j\\ y_j&=f_j+s_j^2\\ k_i&=-2(L'-s_i)\\ b_i&=f_i-(s_i-L')^2\\ \end{aligned}\]

则转移方程就写作 \(b_i = \min_{j<i}\{ y_j-k_ix_j \}\)。我们把 \((x_j,y_j)\) 看作二维平面上的点,则 \(k_i\) 表示直线斜率,\(b_i\) 表示一条过 \((x_j,y_j)\) 的斜率为 \(k_i\) 的直线的截距。问题转化为了,选择合适的 \(j(1\le j<i)\),最小化直线的截距。

\(\Large{\text{ R. 任务安排4.1}}\)

题解:https://www.cnblogs.com/Wy-x/articles/18969298

$\large{\color{purple}点击查看 R. 任务安排4.1 代码}$
#include<bits/stdc++.h>
#define int long long

using namespace std;

const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar()                                                              \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)     \
	? EOF                                                                 \
	: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

int n,s;
int t[1<<20],f[1<<20];
int dp[1<<20];

struct Node{
    int x,y,id;
};
vector<Node> v;

#define X(i) (f[i])
#define Y(i) (dp[i])
#define K(i) (t[i])

int find(const Node x)
{
    int l=0,r=v.size()-1;
    while(l<r)
    {
        int mid=(l+r)>>1;
        if(v[mid].x<x.x) l=mid+1;
        else r=mid-1;
    }
    l-=5;
    r+=5;
    if(l<0) l=0;
    if(r>=v.size()) r=v.size()-1;
    for(int i=r;i>=l;i--)
    if(v[i].x<=x.x) return i+1;
    return 0;
}

signed main()
{
    n=read();
    s=read();
    for(int i=1;i<=n;i++)
    {
        int ti=read(),fi=read();
        t[i]=t[i-1]+ti;
        f[i]=f[i-1]+fi;
    }
    memset(dp,0x3f,sizeof(dp));
    dp[0]=s*f[n];
    v.push_back({X(0),Y(0),0});
    for(int i=1;i<=n;i++)
    {
        while(v.size()>1&&(v[1].y-v[0].y)<=K(i)*(v[1].x-v[0].x)) v.erase(v.begin());
        int p=v[0].id;
        dp[i]=dp[p]+(f[i]-f[p])*t[i]+s*(f[n]-f[i]);
        dp[i]=min(dp[i],dp[0]+(f[i]-f[0])*t[i]+s*(f[n]-f[i]));

        Node nw={X(i),Y(i),i};
        p=find(nw);
        v.insert(v.begin()+p,nw);
        if(p>0&&p<v.size()-1)
        {
            if((v[p+1].y-v[p].y)*(v[p].x-v[p-1].x)<=(v[p].y-v[p-1].y)*(v[p+1].x-v[p].x))
            {
                v.erase(v.begin()+p);
                continue;
            }
        }
        while(p>1&&(v[p].y-v[p-1].y)*(v[p-1].x-v[p-2].x)<=(v[p-1].y-v[p-2].y)*(v[p].x-v[p-1].x))
        {
            v.erase(v.begin()+p-1);
            p--;
        }
        while(p<v.size()-2&&(v[p+2].y-v[p+1].y)*(v[p+1].x-v[p].x)<=(v[p+1].y-v[p].y)*(v[p+2].x-v[p+1].x))
        {
            v.erase(v.begin()+p+1);
            p++;
        }
        
        // while()
        // for(int j=0;j<i;j++)
        // dp[i]=min(dp[i],dp[j]+(f[i]-f[j])*t[i]+s*(f[n]-f[i]));
    }
    cout<<dp[n];
	return 0;
}



\[\Huge{\texttt{大大的分割线}} \]


DP 优化 解题报告

直接线下找我,不想写了。😃

A. [NOIP2012 提高组] 开车旅行

咕。


B. 计算重复Count The Repetitions

咕。


C. Cleaning Shifts清理班次1


D. Cleaning Shifts清理班次2


E. 赤壁之战The Battle of Chibi


F. 围栏Fence


G. 裁剪序列Cut the Sequence


H. 「一本通 5.5 例 2」最大连续和


I. [Scoi2010]股票交易


J. 瑰丽华尔兹


K. [USACO Open11] 修剪草坪


L. [Usaco2013 Nov]Pogo-Cow


M. [tyvj1305]最大子序和


N. [SCOI2009] 生日礼物


O. 任务安排1


posted @ 2025-04-05 20:06  Wy_x  阅读(35)  评论(2)    收藏  举报