https://www.lydsy.com/JudgeOnline/problem.php?id=1233

数据结构优化dp的代码总是那么抽象

题意:奶牛们讨厌黑暗。 为了调整牛棚顶的电灯的亮度,Bessie必须建一座干草堆使得她能够爬上去够到灯泡 。一共有N大包的干草(1<=N<=100000)(从1到N编号)依靠传送带连续的传输进牛棚来。第i包干草有一个 宽度W_i(1<=w_i<=10000)。所有的干草包的厚度和高度都为1. Bessie必须利用所有N包干草来建立起干草堆,并且按照他们进牛棚的顺序摆放。她可以相放多少包就放 多少包来建立起tower的地基(当然是紧紧的放在一行中)。接下来他可以放置下一个草包放在之前一级 的上方来建立新的一级。注意:每一级不能比下面的一级宽。她持续的这么放置,直到所有的草包都被安 置完成。她必须按顺序堆放,按照草包进入牛棚的顺序。说得更清楚一些:一旦她将一个草包放在第二级 ,她不能将接下来的草包放在地基上。 Bessie的目标是建立起最高的草包堆。

Input

第1行:一个单一的整数N。 第2~N+1行:一个单一的整数:W_i。

奥妙重重dp题。

第一眼觉得是个反向贪心,从上往下取,每次取最小的可以形成下一层的干草集合,后来发现是个假算法。

反例

6 
6 2 3 7 10 11

既然贪心不行,就自然而然想到dp,考虑dp[i]表示到这一层的最大层数,发现不可行,因为这样的dp方法不能固定一个状态。

但是这一题有一个很强的证明:对于一座干草堆而言,基底最小的状态一定为最优状态。

也就是说,每一个状态中,地基最小的状态就是高度最高的状态,我们可以简单的推断一下,每一个高度最高的状态必然可以从下往上贪心的堆干草最终达到基底最小的状态。

这样我们原来的dp就变成了dp[i]表示i -- n种所有干草组成的集合形成的最小的干草堆的基底。

我们反向从N - > 1遍历,对于i,需要找到一个最小的大于i的j满足 sum[j - 1] - sum[i - 1] >= dp[j], 这样就变成了一个O(N * N)的dp,但是这还不够,到这一步需要考虑优化。

我们将转移方程变形 变成sum[i - 1] <= sum[j - 1] - dp[j],注意到我们每次需要的考虑的集合在每一次询问i的时候都多加入1个,以及当存在k < j && sum[k - 1] - dp[k]  >= sum[j - 1] - dp[j]的时候,j就是一个多余的状态,sum[i - 1]由于其前缀和的性质,事实上是递减的,对于每一次集合内的查找,我们在查找到最小的j的时候,可以将其余的比sum[i - 1]大的状态全部舍弃。

以上三个性质中,显然可以发现用来维护这个集合的数据结构就是单调队列,最终时间复杂度O(n)

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
#define For(i, x, y) for(int i=x;i<=y;i++)
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))
#define Sca(x) scanf("%d", &x)
#define Sca2(x,y) scanf("%d%d",&x,&y)
#define Scl(x) scanf("%lld",&x);
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x);
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second
typedef vector<int> VI;
const double eps = 1e-;
const int maxn = 1e5 + ;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + ;
int N,M,tmp,K;
int a[maxn];
int sum[maxn];
int dp[maxn];
int f[maxn];
int Queue[maxn];
int main()
{
int N; Sca(N); //sum[i - 1] <= sum[j - 1] - dp[j]
For(i,,N){
Sca(a[i]);
sum[i] = sum[i - ] + a[i];
}
int head = ,tail = ; Queue[] = N + ;
_For(i,N,){
while(head < tail && sum[i - ] <= sum[Queue[head + ] - ] - dp[Queue[head + ]]) head++;
dp[i] = sum[Queue[head] - ] - sum[i - ];
f[i] = f[Queue[head]] + ;
while(head <= tail && sum[i - ] - dp[i] >= sum[Queue[tail] - ] - dp[Queue[tail]]) tail--;
Queue[++tail] = i;
}
printf("%d",f[]);
#ifdef VSCode
system("pause");
#endif
return ;
}

bzoj1233 单调队列优化dp的更多相关文章

  1. 单调队列优化DP,多重背包

    单调队列优化DP:http://www.cnblogs.com/ka200812/archive/2012/07/11/2585950.html 单调队列优化多重背包:http://blog.csdn ...

  2. bzoj1855: [Scoi2010]股票交易--单调队列优化DP

    单调队列优化DP的模板题 不难列出DP方程: 对于买入的情况 由于dp[i][j]=max{dp[i-w-1][k]+k*Ap[i]-j*Ap[i]} AP[i]*j是固定的,在队列中维护dp[i-w ...

  3. hdu3401:单调队列优化dp

    第一个单调队列优化dp 写了半天,最后初始化搞错了还一直wa.. 题目大意: 炒股,总共 t 天,每天可以买入na[i]股,卖出nb[i]股,价钱分别为pa[i]和pb[i],最大同时拥有p股 且一次 ...

  4. Parade(单调队列优化dp)

    题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=2490 Parade Time Limit: 4000/2000 MS (Java/Others)    ...

  5. BZOJ_3831_[Poi2014]Little Bird_单调队列优化DP

    BZOJ_3831_[Poi2014]Little Bird_单调队列优化DP Description 有一排n棵树,第i棵树的高度是Di. MHY要从第一棵树到第n棵树去找他的妹子玩. 如果MHY在 ...

  6. 【单调队列优化dp】 分组

    [单调队列优化dp] 分组 >>>>题目 [题目] 给定一行n个非负整数,现在你可以选择其中若干个数,但不能有连续k个数被选择.你的任务是使得选出的数字的和最大 [输入格式] ...

  7. [小明打联盟][斜率/单调队列 优化dp][背包]

    链接:https://ac.nowcoder.com/acm/problem/14553来源:牛客网 题目描述 小明很喜欢打游戏,现在已知一个新英雄即将推出,他同样拥有四个技能,其中三个小技能的释放时 ...

  8. 单调队列以及单调队列优化DP

    单调队列定义: 其实单调队列就是一种队列内的元素有单调性的队列,因为其单调性所以经常会被用来维护区间最值或者降低DP的维数已达到降维来减少空间及时间的目的. 单调队列的一般应用: 1.维护区间最值 2 ...

  9. BZOJ1791[Ioi2008]Island 岛屿 ——基环森林直径和+单调队列优化DP+树形DP

    题目描述 你将要游览一个有N个岛屿的公园.从每一个岛i出发,只建造一座桥.桥的长度以Li表示.公园内总共有N座桥.尽管每座桥由一个岛连到另一个岛,但每座桥均可以双向行走.同时,每一对这样的岛屿,都有一 ...

随机推荐

  1. codeforces618B

    Guess the Permutation CodeForces - 618B Bob has a permutation of integers from 1 to n. Denote this p ...

  2. CentOS 安装、配置supervisord

    负责在启动自身时启动管理的子进程,响应客户端的命令,重启崩溃或退出的子进程,记录子进程stdout和stderr输出,生成和处理子进程生命周期中的事件. 安装yum install superviso ...

  3. table-layui

    本文章为原创文章,转载请注明出处 html <div class="layui-btn-group tableBtn"> <button class=" ...

  4. 一条命令停止所有lxc容器,删除所有lxc容器

    for i in $(virsh -c lxc:/// list | grep -v 'Id' | awk '{print $2}');do virsh -c lxc:/// destroy ${i} ...

  5. UNIX口令破解机

    在编写我们的UNIX口令破解机时,我们需要使用UNIX 计算口令hash 的crypt()算法.Python 标准库中已自带有crypt 库.要计算一个加密的UNIX 口令hash,只需调用函数cry ...

  6. python模块psutil的使用

    介绍 psutil是一个跨平台库(http://code.google.com/p/psutil/),能够轻松实现获取系统运行的进程和系统利用率(包括CPU.内存.磁盘.网络等)信息.它主要应用于系统 ...

  7. C - A Simple Problem with Integers POJ - 3468 线段树模版(区间查询区间修改)

    参考qsc大佬的视频 太强惹 先膜一下 视频在b站 直接搜线段树即可 #include<cstdio> using namespace std; ; int n,a[maxn]; stru ...

  8. BZOJ2809 dispatching 【可并堆】

    题目分析: yy一下就知道了,合并用可并堆少个log. 代码: #include<bits/stdc++.h> using namespace std; ; int n,m; int b[ ...

  9. altera rom ram IP的浅层理解

    1.altera 提供了两种rom :单口rom和双口rom. 官方文档偷图: 单口rom:输出可以配置寄存器寄存再输出,时钟可以输入输出用不同的时钟. 双口rom:输入输出时钟可不同或者A与B的时钟 ...

  10. new Date()导致日期增加了一天

    问题是:将字符串 "Sun Nov 12 14:00:00 CST 2017"  转成Date类型 有一个简单的方法是直接使用new Date(),但是这个方法对于某些日期的计算, ...