dp单调性优化

  跟着书上的思路学习dp的单调性优化觉得还是很容易想的。

数据范围:

dp,数据范围是百万,这应该是O(n)的算法了。

首先不难想到设f[i]表示到第i个百米所能达到的最大能量,那么f[n]即为所求。

f[i]=max(f[i],f[j]+s[i]-s[j]-cost[i]);这个地方s数组是能量的前缀和,然后发现需要多加一层循环来枚举j,这个时候就是O(n^2)的算法了。

这样的话,就只有40分了,毕竟看分做题。这分给的还是很良心的。

考虑优化首先我们发现状态转移方程可以这样变f[i]=max{f[j]-s[j]}+s[i]-cost[i];我们这需要找到一个最大的f[j]-s[j]的即可

且f[j]还必须>=cost[i];因为这是判断能否调到也就是一个状态合法与否。

这样我们就可以维护一个双端队列来维护了!队首永远最优至于合法否我们需要小小的判断一下。

#include<bits/stdc++.h>
#include<iomanip>
#include<iostream>
#include<ctime>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdio>
#include<vector>
#include<queue>
#include<deque>
#include<map>
#include<stack>
#include<set>
#include<bitset>
#include<cstdlib>
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}
inline void put(int x)
{
    if(x==0){putchar(‘0‘);putchar(‘\n‘);return;}
    if(x<0){putchar(‘-‘);x=-x;}
    int num=0;char ch[50];
    while(x)ch[++num]=x%10+‘0‘,x/=10;
    while(num)putchar(ch[num--]);
    putchar(‘\n‘);return;
}
const int MAXN=2000002;
int v[MAXN],cost[MAXN];
int f[MAXN];//f[i]表示前i百米所能得到的最大能量
int q[MAXN],h=1,t=1;
int n,m;
int main()
{
    //freopen("1.in","r",stdin);
    n=read();m=read();f[0]=m;
    for(int i=1;i<=n;i++)
    {
        v[i]=read();
        cost[i]=i*100;
        v[i]+=v[i-1];
    }
    q[h]=0;
    for(int i=1;i<=n;i++)
    {
        while(f[q[h]]<cost[i])h++;//题目中给出保证一定能吃完,所以这个地方不需要加h<t
        f[i]=f[q[h]]-cost[i]-v[q[h]]+v[i];
        while(f[q[t]]-v[q[t]]<f[i]-v[i])t--;
        q[++t]=i;
    }
    put(f[n]);
    return 0;
}

显然成功了呢,复杂度就是O(n)因为每个点进出队伍一次。

对于这个题目,我竟然直接手残翻开了书,看到了状态转移方程式。哎,自己没想。

其实状态转移方程也很好想,状态设为:f[i][j]表示第i个月存仓j个零件所得到的最低成本。

由此可得,f[i][j]=min{f[i-1][k]+(u[i]+j-k)*d[i]+m*k}其中j<=s,k<=j+u[i].(显然你不能把上个月的零件卖了)

由于k这个决策也需要枚举所以复杂度是O(n*s*s)的,s那么大这肯定炸啊。预期得分也是40。

搞一下优化,大括号里得到貌似都和k有关不如先展开再说。

f[i][j]=min{f[i][k]+(m-d[i])*k}+(u[i]+j)*d[i];这样的话只有f[i][k]和(m-d[i])*k和当前决策有关了,考虑维护一个最优决策那不就可以直接进行转移了么

这样的话就是O(n*s)的啊,能A。所以考虑维护一个单调队列,等等为什么要维护队列,队列中的其他值有用么,发现只要k属于它应该属于的范围之内的话,那么这个决策就一定是合法的,所以为什么要队列,直接一个值保存即可,这样每次和生成出来的值比较哪个更小不就有了最优解么。

这里就大功告成了,乌拉。

#include<bits/stdc++.h>
#include<iomanip>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<queue>
#include<deque>
#include<map>
#include<set>
#include<bitset>
#include<stack>
#include<vector>
#include<cstdlib>
#include<algorithm>
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}
inline void put(int x)
{
    if(x==0){putchar(‘0‘);putchar(‘\n‘);return;}
    if(x<0)x=-x,putchar(‘-‘);
    int num=0;char ch[50];
    while(x)ch[++num]=x%10+‘0‘,x/=10;
    while(num)putchar(ch[num--]);
}
const int MAXN=100;
int n,m,s,p=0;
int u[MAXN],d[MAXN];
int f[2][10002];//f[i][j]表示第i个月保存j个商品所需最小费用。
int main()
{
    //freopen("1.in","r",stdin);
    n=read();m=read();s=read();
    for(int i=1;i<=n;i++)u[i]=read();
    for(int i=1;i<=n;i++)d[i]=read();
    memset(f,10,sizeof(f));
    f[p][0]=0;
    for(int i=1;i<=n;i++)
    {
        p=p^1;
        int k=0,ans=2147483646;
        for(int j=0;j<=s;j++)//枚举保存多少个商品
        {
            for(;k<=min(j+u[i],s);k++)//提供状态转移
            //为什么最优的状态一定是ans呢?
            //考虑到需要一个k值使ans最小且合法,合法那一定是<=min(j+u[i],s)
            //得到ans就是最优的了,很显然吧,复杂度为n*s
                ans=min(ans,f[p^1][k]+(m-d[i])*k);
            f[p][j]=ans+(u[i]+j)*d[i];
        }
    }
    put(f[p][0]);
    return 0;
}

显然的,我们发现当前状态只和上一个状态有关所以可以开滚动数组优化空间啊。这点完美意识还是需要的。

那么经过这两道题,相信单调性优化都了解的差不多了。可以深入学习一些其他优化了呢。

原文地址:https://www.cnblogs.com/chdy/p/10200250.html

时间: 2024-11-10 06:37:14

dp单调性优化的相关文章

dp 单调性优化总结

对于单调性优化其实更多的是观察dp的状态转移式子的单调性 进而用优先队列 单调队列 二分查找什么的找到最优决策 使时间更优. 对于这道题就是单调性优化的很好的例子 首先打一个暴力再说. f[i][j]表示前i个木匠刷前j个木板所得到的最大价值 f[i][j]=max(f[i][j],f[i-1][j]); f[i][j]=max(f[i][j],f[i][j-1]) f[i][j]=max(f[i][j],f[i-1][k]+(j-k)*r(j));(k<t(j)<=j)&&(

决策单调性优化dp 专题练习

决策单调性优化dp 专题练习 优化方法总结 一.斜率优化 对于形如 \(dp[i]=dp[j]+(i-j)*(i-j)\)类型的转移方程,维护一个上凸包或者下凸包,找到切点快速求解 技法: 1.单调队列 : 在保证插入和查询的x坐标均具有单调性时可以使用 2.单调栈+二分:保证插入有单调性,不保证查询有单调性 3.分治+ 1 或 2:在每次分治时将\([l,mid]\)这段区间排序后插入,然后更新右区间\([mid+1,r]\)的答案 二.分治.单调队列维护有单调性的转移 (甚至还有分治套分治)

DP 斜率优化题目/决策单调性题目

斜率优化 冬令营讲到了dp斜率优化后瞎写了一些斜率优化,因为毕竟上次写是老早以前了,当时对这个并不是很理解,现在有一点数学基础,稍微好一点了. 瞎找的斜率优化题,可能有点太水了,我这种这么菜的人竟然都切得那么快.可能难度不够,因为有个y坐标不单调要套个平衡树的我是真不会,这个嘛以后再说了. 1.hdu3507 Print Article 给定非负数列,划成若干块,每块价值是左边那玩意儿.求总权值min. 这个自己写写就可以搞出来了吧,现在看真心不难,除非数值有负的..那个要平衡树,现在写不动.

算法优化》关于1D*1D的DP的优化

关于这一主题的DP问题的优化方法,我以前写过一篇博客与其有关,是关于对递推形DP的前缀和优化,那么这种优化方法就不再赘述了. 什么叫1D*1D的DP捏,就是一共有N种状态,而每种状态都要N种决策,这就叫1D*1D的DP,对于这种DP一般来说状态的转移都是可以从O(N2)优化到O(NlogN)甚至O(N)的,那么,针对于不同的情况,也有不同的优化方法 经典模型一:(b[x]随x非降) 对于DP方程形似与这一种的方程,明显的,这种DP的抉择方案数可以用单调队列直接解决化为O(1),所以对于这样的转移

DP常用优化

DP常用优化 一.前缀和优化 当遇到类似:\(f[i] = \sum_{j = k}^{i} g[j]\)的转移时,可以通过预处理出\(g[i]\)的前缀和\(s[i]\),将\(O(n)\)的求和转换为\(O(1)?\)的操作. [HAOI2009]逆序对数列 [HAOI2008]木棍分割 二分答案+dp P4099 [HEOI2013]SAO 树形dp 二.决策单调性--单调队列优化 接下来几种优化方法主要是对1d/1d dp的优化,其中xd/yd dp指的是状态数有\(n^x\)种,每个状

P3515 [POI2011]Lightning Conductor[决策单调性优化]

给定一序列,求对于每一个$a_i$的最小非负整数$p_i$,使得$\forall j \neq i $有$ p_i>=a_j-a_i+ \sqrt{|i-j|}$. 绝对值很烦 ,先分左右情况单独做.现在假设j都在i左边,则$p_i=max{a_j-a_i+ \sqrt{i-j}}=max{a_j+ \sqrt{i-j} }-a_i$.带根号,不易斜率优化,考虑证决策单调性. 假设最优决策为j,j之前的任意决策称之为$j'$,则有 $f[j]+\sqrt{i-j} \geqslant f[j']

学渣乱搞系列之dp斜率优化

学渣乱搞系列之dp斜率优化 By 狂徒归来 貌似dp的斜率优化一直很难搞啊,尤其是像我这种数学很挫的学渣,压根不懂什么凸包,什么上凸下凸的,哎...说多了都是泪,跟wdd讨论了下,得出一些结论.本文很大部分参考了大神Accept的文章,不过此神貌似早已绝迹江湖,这篇文章写得好,也写得很差,前半部分叙述得很好,可是关键,关键部分说得很乱,有些许错误,很多大神都进行了评论指出,但是大神Accept貌似没有修改的意思,故重新总结下,以便自己以后查阅和复习啊. 下面看一个例题Print Article.

BZOJ 1096: [ZJOI2007]仓库建设(DP+斜率优化)

[ZJOI2007]仓库建设 Description L公司有N个工厂,由高到底分布在一座山上.如图所示,工厂1在山顶,工厂N在山脚.由于这座山处于高原内陆地区(干燥少雨),L公司一般把产品直接堆放在露天,以节省费用.突然有一天,L公司的总裁L先生接到气象部门的电话,被告知三天之后将有一场暴雨,于是L先生决定紧急在某些工厂建立一些仓库以免产品被淋坏.由于地形的不同,在不同工厂建立仓库的费用可能是不同的.第i个工厂目前已有成品Pi件,在第i个工厂位置建立仓库的费用是Ci.对于没有建立仓库的工厂,其

【BZOJ-4518】征途 DP + 斜率优化

4518: [Sdoi2016]征途 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 230  Solved: 156[Submit][Status][Discuss] Description Pine开始了从S地到T地的征途. 从S地到T地的路可以划分成n段,相邻两段路的分界点设有休息站. Pine计划用m天到达T地.除第m天外,每一天晚上Pine都必须在休息站过夜.所以,一段路必须在同一天中走完. Pine希望每一天走的路长度尽可能相近,所以他