题意:给你一个长度为n的数列,你需要把这个数列分成几段,每段的和不超过m,问各段的最大值之和的最小值是多少?

思路:dp方程如下:设dp[i]为把前i个数分成合法的若干段最大值的最小值是多少。dp转移比较显然,dp[i] = min{dp[j] + max(a[j + 1] , a[j + 2] ... + a[i])}, 其中a[j + 1] + a[j + 2] +... + a[i] <= m;这个dp转移是O(n^2)的,我们需要用单调队列优化。单调队列维护的是a值单调递减的序列(要保证与i位置的区间和小于等于m)而单调队列的对头不一定是最优的。需要找出单调队列中的最小值,这个需要用堆或者线段树来维护一下。dp[i]的转移分为两种,一种是j + 1 到i的和正好小于m的这种转移,另一种是单调队列中的最小值,两者取min就是当前状态的最小值。

这题有两个点需要注意。1:若j在单调队列里,那么max(a[j + 1] , a[j + 2] ... + a[i])是单调队列里的下一个值。2:因为max(a[j + 1] , a[j + 2] ... + a[i])这个值是有可能随i的变化而变化,所以,如果用堆去维护单调队列中的值, 需要对每个j记录一下最新的max(a[j + 1] , a[j + 2] ... + a[i]), 不能直接扔到堆里就完事了。。。或者,使用pbds中的堆,它支持对堆中元素的修改,然而POJ不支持pbds。。。。

一般堆的代码:

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define LL long long
#define pii pair<int, int>
#define lowbit(x) (x << 1)
#define ls(x) (x << 1)
#define rs(x) ((x << 1) | 1)
#define db double
#define pli pair<LL, int>
using namespace std;
const int maxn = 100010;
struct node {LL val;int pos;bool operator < (const node & rhs) const {return val > rhs.val;}
};
priority_queue<node> Q;
LL dp[maxn], a[maxn];
int q[maxn];
bool v[maxn];
LL val[maxn];
LL sum[maxn];
void change(LL x, int y) {Q.push((node){x, y});val[y] =  x;
}
int main() {int n;LL m;scanf("%d%lld", &n, &m);for (int i = 1; i <= n; i++) {scanf("%lld", &a[i]);sum[i] = sum[i - 1] + a[i];}int l = 1, r = 1, ans = 0, pos = 0;dp[1] = a[1];q[1] = 1;if(a[1] > m) ans = -1;for (int i = 2; i <= n; i++) {while(sum[i] - sum[pos] > m) pos++;if(pos == i) {ans = -1;break;}while(l <= r && sum[i] - sum[q[l] - 1] > m) {v[q[l]] = 1;l++;}while(l <= r && a[q[r]] <= a[i]) {v[q[r]] = -1;r--;}if(l <= r)change(dp[q[r]] + a[i], q[r]);q[++r] = i;dp[i] = dp[pos] + a[q[l]];while(Q.size() && (v[Q.top().pos] == 1 || val[Q.top().pos] != Q.top().val)) {Q.pop();}if(Q.size()) {dp[i] = min(dp[i], Q.top().val);}}if(ans == -1) {printf("%d\n", ans);} else {printf("%lld\n", dp[n]);}
}

pb_ds的代码(应该是对的吧)

#include <bits/stdc++.h>
#define LL long long
#define pii pair<int, int>
#define lowbit(x) (x << 1)
#define ls(x) (x << 1)
#define rs(x) ((x << 1) | 1)
#define db double
#define pli pair<LL, int>
#include <ext/pb_ds/priority_queue.hpp>
using namespace std;
using namespace __gnu_pbds;
const int maxn = 100010;
struct node {LL val;int pos;bool operator < (const node & rhs) const {return val > rhs.val;}
};
typedef __gnu_pbds::priority_queue<node> Heap;
Heap Q;
Heap::point_iterator id[maxn];
LL dp[maxn], a[maxn];
int q[maxn];
bool v[maxn];
LL sum[maxn];
void change(LL x, int y) {if(id[y] != 0)Q.modify(id[y], (node){x, y});else id[y] = Q.push((node){x, y});
}
int main() {int n, m;//freopen("17.in", "r", stdin);scanf("%d%d", &n, &m);for (int i = 1; i <= n; i++) {scanf("%d", &a[i]);sum[i] = sum[i - 1] + a[i];}int l = 1, r = 1, ans = 0, pos = 0;dp[1] = a[1];q[1] = 1;if(a[1] > m) ans = -1;for (int i = 2; i <= n; i++) {while(sum[i] - sum[pos] > m) pos++;if(pos == i) {ans = -1;break;}while(l <= r && sum[i] - sum[q[l] - 1] > m) {v[q[l]] = 1;l++;}if(l > r) {ans = -1;break;}while(l <= r && a[q[r]] <= a[i]) {v[q[r]] = 1;r--;}
//      Q.push(make_pair(dp[pos] + a[i], a[q[l]]));
//      printf("%d\n", Q.size());if(l <= r)change(dp[q[r]] + a[i], q[r]);q[++r] = i;dp[i] = dp[pos] + a[q[l]];while(Q.size() && v[Q.top().pos]) {id[Q.top().pos] = 0;Q.pop();}if(Q.size()) {//printf("%lld %d\n", Q.top().val, Q.top().pos);dp[i] = min(dp[i], Q.top().val);}}for (int i = 1; i <= n; i++)printf("%d %lld\n", i, dp[i]);if(ans == -1) {printf("%d\n", ans);} else {printf("%lld\n", dp[n]);}
}

  

转载于:https://www.cnblogs.com/pkgunboat/p/10752884.html

POJ 3017 DP + 单调队列 + 堆相关推荐

  1. POJ 1821 Fence ★(单调队列优化DP)

    题目大意:有一道线性篱笆由N个连续的木板组成.有K个工人,你要叫他们给木板涂色.每个工人有3个参数:L 表示 这个工人可以涂的最大木板数目,S表示这个工人站在哪一块木板,P表示这个工人每涂一个木板可以 ...

  2. POJ - 3926 Parade(单调队列优化dp)

    题目链接:点击查看 题目大意:给出一个n*m的街道,其中有(n+1)*m条街道,每条街道都有一个值,现在我们需要从最下面的任意一点出发,到达最上面的任意一点结束,问如何规划路线能让沿途经过的街道的权值 ...

  3. POJ 1821 Fence(单调队列优化DP)

    题解 以前做过很多单调队列优化DP的题. 这个题有一点不同是对于有的状态可以转移,有的状态不能转移. 然后一堆边界和注意点.导致写起来就很难受. 然后状态也比较难定义. dp[i][j]代表前i个人涂 ...

  4. URAL 1427. SMS(DP+单调队列)

    题目链接 我用的比较传统的办法...单调队列优化了一下,写的有点搓,不管怎样过了...两个单调队列,存两个东西,预处理一个标记数组存... 1 #include <iostream> 2 ...

  5. 烽火传递(dp+单调队列)

    烽火台又称烽燧,是重要的军事防御设施,一般建在险要或交通要道上.一旦有敌情发生,白天燃烧柴草,通过浓烟表达信息:夜晚燃烧干柴,以火光传递军情,在某两座城市之间有 n 个烽火台,每个烽火台发出信号都有一 ...

  6. POJ 2823-Sliding Window单调队列解题报告

    链接:http://poj.org/problem?id=2823 利用单调队列的出队入队,维护区间的最值,保证队列单调递增或单调递减,要维护单调递增队列,当一个数字插入的时候,从队尾往前找到第一个比 ...

  7. [DP/单调队列]BZOJ 2059 [Usaco2010 Nov]Buying Feed 购买饲料

    首先我想吐槽的是题目并没有表明数据范围... 这个题目 DP方程并不难表示. dp[i][j]表示前i个地点携带了j个货物的最小花费 dp[i][j] = dp[i-1][k] + (j-k) * c ...

  8. bzoj1791,P4381-[IOI2008]Island【基环树,树形dp,单调队列dp,树的直径】

    正题 评测记录:https://www.luogu.org/recordnew/lists?uid=52918&pid=P4381 题目大意 有n个岛,n条无向边(保证每个岛都有边连到).走过 ...

  9. bzoj2500幸福的道路 树形dp+单调队列

    2500: 幸福的道路 Time Limit: 20 Sec  Memory Limit: 256 MB Submit: 434  Solved: 170 [Submit][Status][Discu ...

最新文章

  1. pyqt漂亮gui界面模板_一种基于模板的C代码自动生成方法
  2. tensorflow 语义slam_【论文阅读28】DynaSLAM
  3. 奇偶ASCII值判断(信息学奥赛一本通-T1042)
  4. opencv 亮度非线性变化 一
  5. 翻转子串(important!)
  6. 我的15年操作系统开源路——RT-Thread 创始人熊谱翔
  7. Flutter 基础篇-所有知识点架构
  8. BZOJ5074 小B的数字
  9. Python验证码识别:利用pytesser识别简单图形验证码
  10. Java连接数据库代码
  11. Python实现LBP算法
  12. 力天创见客流统计标书制作
  13. 总结软考(软件设计师中级)——计算机语言基础的知识
  14. 施努卡:锂电池模组生产线(锂电池模组是什么)
  15. ADAS常见缩略词(FCW/LKA/LCA/LDW...)
  16. css实现宽高比固定
  17. 计算机应用程序无响应,电脑的程序未响应是什么原因
  18. 微信小程序IOS sticky 兼容写法
  19. 让机器认知中文实体 — 复旦大学知识工场发布中文实体识别与链接服务
  20. 为什么要配置hugepages?

热门文章

  1. hdu1556(树状数组小地方的解释~~~)
  2. 大学计算机人工智能知识点,人工智能知识点大集锦
  3. Java对象析构_c++之对象构造顺序和销毁(析构函数)
  4. 初识Frida--Android逆向之Java层hook (一)
  5. JNI实现源码分析【二 数据结构】
  6. homeassistant树莓派cpu_集成ESP8266的WiFi RGB灯泡接入Home Assistant
  7. Qt 读取XML文档的简单示例
  8. webstorm怎么跑项目_怎么跑Mint-UI的实例,你知道吗?
  9. pythonmainoffset_python之(22)基础总结(5)
  10. 单词接龙pascal程序