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 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)

  1. #include <map>
  2. #include <set>
  3. #include <ctime>
  4. #include <cmath>
  5. #include <queue>
  6. #include <stack>
  7. #include <vector>
  8. #include <string>
  9. #include <cstdio>
  10. #include <cstdlib>
  11. #include <cstring>
  12. #include <sstream>
  13. #include <iostream>
  14. #include <algorithm>
  15. #include <functional>
  16. using namespace std;
  17. #define For(i, x, y) for(int i=x;i<=y;i++)
  18. #define _For(i, x, y) for(int i=x;i>=y;i--)
  19. #define Mem(f, x) memset(f,x,sizeof(f))
  20. #define Sca(x) scanf("%d", &x)
  21. #define Sca2(x,y) scanf("%d%d",&x,&y)
  22. #define Scl(x) scanf("%lld",&x);
  23. #define Pri(x) printf("%d\n", x)
  24. #define Prl(x) printf("%lld\n",x);
  25. #define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
  26. #define LL long long
  27. #define ULL unsigned long long
  28. #define mp make_pair
  29. #define PII pair<int,int>
  30. #define PIL pair<int,long long>
  31. #define PLL pair<long long,long long>
  32. #define pb push_back
  33. #define fi first
  34. #define se second
  35. typedef vector<int> VI;
  36. const double eps = 1e-;
  37. const int maxn = 1e5 + ;
  38. const int INF = 0x3f3f3f3f;
  39. const int mod = 1e9 + ;
  40. int N,M,tmp,K;
  41. int a[maxn];
  42. int sum[maxn];
  43. int dp[maxn];
  44. int f[maxn];
  45. int Queue[maxn];
  46. int main()
  47. {
  48. int N; Sca(N); //sum[i - 1] <= sum[j - 1] - dp[j]
  49. For(i,,N){
  50. Sca(a[i]);
  51. sum[i] = sum[i - ] + a[i];
  52. }
  53. int head = ,tail = ; Queue[] = N + ;
  54. _For(i,N,){
  55. while(head < tail && sum[i - ] <= sum[Queue[head + ] - ] - dp[Queue[head + ]]) head++;
  56. dp[i] = sum[Queue[head] - ] - sum[i - ];
  57. f[i] = f[Queue[head]] + ;
  58. while(head <= tail && sum[i - ] - dp[i] >= sum[Queue[tail] - ] - dp[Queue[tail]]) tail--;
  59. Queue[++tail] = i;
  60. }
  61. printf("%d",f[]);
  62. #ifdef VSCode
  63. system("pause");
  64. #endif
  65. return ;
  66. }

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. ASP.NET Core 2.0 Cookie Authentication

    using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Builder; using Microso ...

  2. 基准对象object中的基础类型----数字 (二)

    object有如下子类: CLASSES object basestring str unicode buffer bytearray classmethod complex dict enumera ...

  3. Python多进程、多线程、协程

    转载:https://www.cnblogs.com/huangguifeng/p/7632799.html 首先我们来了解下python中的进程,线程以及协程! 从计算机硬件角度: 计算机的核心是C ...

  4. OpenCv的CV2一些函数总结

  5. python中可变参数和关键字参数总结

    #_*_coding='utf-8' #可变参数 def person(name,age,*args): #定义了可变参数args print('传入的名字为:',name) print('传入的年龄 ...

  6. Marriage Match II HDU - 3081(二分权值建边)

    题意: 有编号为1~n的女生和1~n的男生配对 首先输入m组,a,b表示编号为a的女生没有和编号为b的男生吵过架 然后输入f组,c,d表示编号为c的女生和编号为d的女生是朋友 进行配对的要求满足其一即 ...

  7. Linux查看实时网卡流量的几种方式

    Linux查看实时网卡流量的几种方式 来源  https://www.jianshu.com/p/b9e942f3682c 在工作中,我们经常需要查看服务器的实时网卡流量.通常,我们会通过这几种方式查 ...

  8. jQuery File Upload 图片上传解决方案兼容IE6+

    1.下载:https://github.com/blueimp/jQuery-File-Upload 2.命令: npm install bower install ================= ...

  9. linux(fedora) 第二课

    vi 文件名(一般模式 编辑模式 命令模式) 一般模式 (不可编辑 按 a,i,o可以切换为编辑模式): 1.删除n行:按下想删除的行数+dd 编辑模式(编辑模式 可以浏览,查找,编辑 按‘:” 可以 ...

  10. DHU--1247 Hat’s Words && HiHocder--1014 Trie树 (字典树模版题)

    题目链接 DHU--1247 Hat’s Words HiHocder--1014 Trie树 两个一个递归方式一个非递归 HiHocoder #include<bits/stdc++.h> ...