DPスロープ$ $ Luanjiangの最適化

最適化のスロープ\(DP \) Luanjiang

スロープの最適化については、私はああ、私は本当に理解していない馬鹿だったんだ......

それを理解するのに役立つ何かを書く......二つの例の合計、非常に典型的なもので、私は最初の細部に入る、他はそれについて少し話します。

\(T1 [HNOI2018] TOYの\ ) おもちゃパッキング

そして、状態方程式は、ああのうち非常に良いです。

\([I] =分F (\ [J] +(S [i]を-s [J] + IJL-1)^ 2)ここで、f) ここで、\(S [I]が\)プレフィックスを表し、\(F [I] \)前方を表す\(Iは\)処理後の最小値。

転送したい場合は、私たちはこの事を見つけました\(O(N ^ 2) \) と\(N <= 50000 \) 明らかに転送されていません。

次のステップでは、世界の傾きを最適化することです!

我々が探していることである\(私は\)前の\(J \)ので、([I] \ F)\最低限、あなたはすぐに見つけることができる方法を検討します。

\([I] = sの[I] + I、B [i]は= Sで[I] + IL-1 \)

私たちは元の方程式の変形、我々は、このフォームを取得することができます:(なぜないのパイプオーケー)

\(2 * A [I] * B [J] + F [I] -a [I] = F ^ 2 [J] + B [J] ^ 2 \)我々は転送するとき、明確にするために\を(私は\)とき\([I] \)が決定されます。

対応する約\(KX + B = Y \) 我々は聞かせて\(2 * [I] \) の\(K \)、\ (B [J] \)\(X \)、\ (F [ J] + B [J] ^ 2 \) の\(Y \) そして私たちは、直線を求める\(Y \)切片上の最小値。

我々は転送すると(私は\)\を、前述の\(1〜I-1 \は ) スタックポイント(として表すことができる\(P_j(B [J]、F [J] + B [J] ^ 2) \) )。

それが探しを通じて、転送され\(P_j \)の傾き\(2 * [I] \) それように、直線の\(Y \)軸が最小化されるインターセプト。

上方凸包(例えば、右上のパネル)を構成する、3つ指摘したい、我々は直線が翻訳される場合、ことが判明\(Bの\)が最適な決定はなりません。

私たちはそれを与えることができる\(B \)点を(図に示すように)、即ち、下方に凸包を維持するために、我々は、この場合に見出される\(A、B、C \ ) 三時には、最も可能性が高いです優れた意思決定。

この時点で、すべての考えメンテナンスは、データ構造を持つ点の下方に凸包を構成することができます。

//回来看下之前我们说的斜率\(2*a[i]\),显然它具有单调性(如果没有单调性就二分)

在前\(1~i-1\)中,不妨设有两个状态:\(j,k\)\(j\)\(k\)更优,则有:

\(f[j]+(s[i]-s[j]+i-j-L-1)^2<f[k]+(s[i]-s[k]+i-k-L-1)^2\)

经过一系列毫不人道的化简(风骨傲天很懒所以他没把将过程放上来)可以得到:

\(2*a[i]>\frac{f[j]-f[k]+b[j]^2-b[k]^2}{b[j]-b[k]}\)(好丑的式子……)

也就是说只要满足这个式子,就有\(j\)\(k\)更优。

同样对应斜率,就有:\(P_j\)\(P_k\)的斜率小于\(2*a[i]\)时,就有\(j\)\(k\)更优。

又因为我们维护的是一个向下的凸包,所以我们就只用考虑相邻的两个点即可。

那么我们就可以用一个单调队列来维护,队列中相邻点间连线的斜率递增。

这张图应该说很好的反应了转移和维护的过程,上面的两张就是转移,下面的两张是维护。

转移:由图中我们可以发现,我们要求的\(j\)为斜率第一个大于\(2*a[i]\)的点,因此舍掉\(A,B\)

维护:因为单桥队列中斜率的单调性,删掉\(E\),由转移后的\(i\)得出的点\(F(b[i],f[i]+b[i]^2)\)加入队尾。

而实际上,我们的斜率优化有一定的公式:

当方程形如:\(f[i]=min(f[j]+S(i,j))+k\)\(k\)为常数)时,可以使用斜率优化。我们的斜率为在当次转移中的一个不变量,维护的是一堆点。不过通常用上面讲到的“假设两个状态法”来求斜率。

所以斜率优化的思考过程应该是和上面讲到的相反,先考虑斜率再变形方程并得出\(x,y\)

啊~终于打完了,累成狗

又是快乐的代码时间:

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    int f=1,w=0;char x=0;
    while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();}
    while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();}
    return w*f;
}
const int N=50010;
int n,q[N],l,r,L;
double s[N],f[N];
inline double A(int i) {return s[i]+i;}
inline double B(int i) {return s[i]+i+L+1;}
inline double S(double x) {return x*x;}
inline double K(int i,int j) {return (f[i]-f[j]+S(B(i))-S(B(j)))/(B(i)-B(j));}
int main(){
#ifndef ONLINE_JUDGE
    freopen("A.in","r",stdin);
#endif
    n=read();L=read();l=r=1;
    for(int i=1,c;i<=n;i++) c=read(),s[i]=c*1.0+s[i-1];
    for(int i=1;i<=n;i++)
    {
        while(l<r&&2*(s[i]+i)>K(q[l],q[l+1])) l++;
        f[i]=f[q[l]]+S(A(i)-B(q[l]));
        while(l<r&&K(q[r-1],q[r])>K(i,q[r-1])) r--;
        q[++r]=i;
    }
    printf("%lld",(long long)f[n]);
}

\(T2APIO2010\)特别行动队

这一题也是一个经典的斜率优化,稍微有点不同。

状态:\(f[i]=min(f[j]+a*(s[i]-s[j])^2+b*(s[i]-s[j])+c)\)

化简:\(2*a*s[i]*s[j]+f[i]-a*s[i]^2-b*s[i]-c=f[j]+a*s[j]^2-b*s[j]\)

因为这一题的\(a<0\),所以我们维护一个向上的凸包即可。

直接上代码:(我说过了,这个题解只会简单说明哦\(QwQ\)

#include <cstdio>
using namespace std;
#define F(x) ((x)*(x))
inline int read()
{
    int f=1,w=0;char x=0;
    while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();}
    while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();}
    return w*f;
}
const int N=1000010;
#define int long long
int n,l,r,f[N],Q[N],a,b,c,s[N];
inline double Work(int x,int y)
{
    return 1.*(f[x]-f[y]+(F(s[x])-F(s[y]))*a)/(s[x]-s[y])-b;
}
main(){
    n=read(),a=read(),b=read(),c=read();
    for(int i=1;i<=n;i++) s[i]=read(),s[i]+=s[i-1];
    for(int i=1;i<=n;i++)
    {
        while(l<r&&Work(Q[l+1],Q[l])>s[i]*2*a) l++;
        //用于从队列中选出最值
        f[i]=f[Q[l]]+a*F(s[i]-s[Q[l]])+b*(s[i]-s[Q[l]])+c;
        while(l<r&&Work(Q[r],Q[r-1])<Work(i,Q[r])) r--;
        //维护队列单调性
        Q[++r]=i;
    }
    printf("%lld",f[n]);
}

おすすめ

転載: www.cnblogs.com/wo-shi-zhen-de-cai/p/11015133.html