动态规划专题 - 解题报告 - L

转移方程: d p [ i ] = m i n ( d p [ i ] , d p [ j ] + ( x − L ) 2 ) , 0 &lt; = j &lt; = i − 1 dp[i]=min(dp[i],dp[j]+(x-L)^2) , 0&lt;=j&lt;=i-1 dp[i]=min(dp[i],dp[j]+(xL)2),0<=j<=i1
但是如果只是单纯的n2 爆破大概率超时,(但就是有爆破神仙过了),这里的优化想法是这样的,如果说能直接找到i对应的j中最优秀的那个j就能很好地优化这个过程。
先处理一下前缀和,sum[i]表示第i位及以前的体积和,那么原式即为:
d p [ i ] = m i n ( d p [ i ] , d p [ j ] + ( s u m [ i ] + i − s u m [ j ] − j − L − 1 ) 2 ) ( j &lt; i ) dp[i]=min(dp[i],dp[j]+(sum[i]+i−sum[j]−j−L−1) ^2)(j&lt;i) dp[i]=min(dp[i],dp[j]+(sum[i]+isum[j]jL1)2)(j<i)
我们想要作为j的两个选择中q点比p点更优,那么就有:
d p [ q ] + ( s u m [ i ] + i − s u m [ q ] − q − L − 1 ) 2 &lt; d p [ p ] + ( s u m [ i ] + i − s u m [ p ] − p − L − 1 ) 2 dp[q]+(sum[i]+i−sum[q]−q−L−1) ^2&lt;dp[p]+(sum[i]+i−sum[p]−p−L−1) ^2 dp[q]+(sum[i]+isum[q]qL1)2<dp[p]+(sum[i]+isum[p]pL1)2

化简如下,令:
k ( i ) = s u m [ i ] + i ; k(i)=sum[i]+i; k(i)=sum[i]+i;
h ( i ) = s u m [ i ] + i − L − 1 h(i)=sum[i]+i-L-1 h(i)=sum[i]+iL1
所以原式即为: d p [ q ] + ( k ( i ) − h ( q ) ) 2 &lt; d p [ p ] + ( k ( i ) − h ( p ) ) 2 dp[q]+(k(i)-h(q))^2&lt;dp[p]+(k(i)-h(p))^2 dp[q]+(k(i)h(q))2<dp[p]+(k(i)h(p))2
即 : 2 ⋅ k ( i ) &gt; d p [ q ] + h ( q ) 2 − d p [ p ] − h ( q ) 2 h ( q ) − h ( p ) 即: 2\cdot k(i)&gt;\frac{{dp[q]+h(q)^{2} - dp[p]-h(q)^{2}}}{{ h(q)-h( p)}} 2k(i)>h(q)h(p)dp[q]+h(q)2dp[p]h(q)2
g ( i ) = d p [ i ] + h ( i ) 2 g(i) = dp[i]+h(i)^2 g(i)=dp[i]+h(i)2:
最 终 化 简 为 : 2 ⋅ k ( i ) &gt; g ( q ) − g ( p ) h ( q ) − h ( p ) 最终化简为: 2\cdot k(i)&gt;\frac{{g(q)-g(p)}}{{ h(q)-h( p)}} 2k(i)>h(q)h(p)g(q)g(p)

( g ( q ) − g ( p ) / ( h ( q ) − h ( p ) ) &lt; 2 ⋅ K ( i ) (g( q )-g( p )/(h( q )-h( p ))&lt;2\cdot K( i ) (g(q)g(p)/(h(q)h(p))<2K(i)
• g(x),h(x),k(x)都是函数。
• 这是不是很像斜率?
• 我们判断两点哪个更优秀是不是可以通过
(h( p ),g( p ))和(h( q ) ,g( q ))两点间斜率大小来判断?

题解的意思已经很明了了,有了斜率的辅助,现在目标就只剩找到最优点了。
那么哪个点是当前最优点呢?显而易见,必须左斜率小于等于k(i)并且右斜率大于等于k(i)。我们看的出来这是一个凸包,满足单调性,且左边的点删了就删了不会影响,所以我们可以维护一个单调队列可以大幅度减少搜索时间。
至于删点的条件,如果补入的新点与之前的凸包构成图形不是凸包了,那么内凹的点就可以删了,这需要三个点来判断是否内凹,que[ tail ], que[ tail - 1 ], 和 i 点。
所以就能写出:

for (int i = 1; i <= n; i++)
    {
        while (head < tail && judge1(head, i)) head++;		
        dp[i] = dp[que[head]] + s(i, que[head]);			//单调队列中同步更新dp[i]
        while (head < tail && judge2(que[tail - 1], que[tail], i)) tail--;
        que[++tail] = i;
    }

然后写一下判断添加函数和判断删除函数,这里题目要求是不要用double,用long long,叉乘判断斜率大小即可,好像说是ll相乘会炸??但是我没遇到,大爷说是我跟着stl一起爆的……emmm不是很懂,但我还是喜欢叉乘不喜欢浮点运算

bool judge1(ll head, ll i){						//判断添加
    ll up = g(que[head]) - g(que[head + 1]);
    ll down = h(que[head]) - h(que[head + 1]);
    if (up > down * 2 * k(i))
        return true;
    return false;
}
bool judge2(ll p1, ll p2, ll p3){				//判断删除
    return (h(p2) - h(p3)) * (g(p1) - g(p3)) - (h(p1) - h(p3))
        * (g(p2) - g(p3)) >= 0;
}

最后贴AC代码:

#include<bits/stdc++.h>
#define FOR(a, b, c) for(int a=b; a<=c; a++)
#define maxn 50005
#define maxm 55
#define hrdg 1000000007
#define inf 2147483647
#define llinf 9223372036854775807
#define ll long long
#define pi acos(-1.0)
#define ls p<<1
#define rs p<<1|1
using namespace std;

ll n, L, v[maxn], sum[maxn], dp[maxn];
ll head, tail, que[maxn];

inline ll read(){
    char c=getchar();long long x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}

ll k(ll x) {return sum[x] + x;}
ll h(ll x) {return k(x) + L + 1;}
ll g(ll x){
    ll temp = h(x);
    return dp[x] + temp * temp;
}
ll s(ll x, ll y) {
    ll temp = k(x) - h(y);
    return temp * temp;
}

bool judge1(ll head, ll i){
    ll up = g(que[head]) - g(que[head + 1]);
    ll down = h(que[head]) - h(que[head + 1]);
    if (up > down * 2 * k(i))
        return true;
    return false;
}
bool judge2(ll p1, ll p2, ll p3){
    return (h(p2) - h(p3)) * (g(p1) - g(p3)) - (h(p1) - h(p3))
        * (g(p2) - g(p3)) >= 0;
}

int main()
{
    n = read(); L = read();
    for (ll i = 1; i <= n; i++)
    {
        v[i] = read();
        sum[i] = sum[i - 1] + v[i];
    }
    //FOR(i, 1, n) cout<<sum[i]<<endl;
    head = tail = 1;
    for (ll i = 1; i <= n; i++)
    {
        while (head < tail && judge1(head, i)) head++;
        dp[i] = dp[que[head]] + s(i, que[head]);
        while (head < tail && judge2(que[tail - 1], que[tail], i)) tail--;
        que[++tail] = i;
    }
    printf("%lld\n", dp[n]);
    return 0;
}

/*
5 4
3 4 2 1 4
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值