POJ 3017 单调队列dp

Cut the Sequence

Time Limit: 2000MS   Memory Limit: 131072K
Total Submissions: 8764   Accepted: 2576

Description

Given an integer sequence { an } of length N, you are to cut the sequence into several parts every one of which is a consecutive subsequence of the original sequence. Every part must satisfy that the sum of the integers in the
part is not greater than a given integer M. You are to find a cutting that minimizes the sum of the maximum integer of each part.

Input

The first line of input contains two integer N (0 < N ≤ 100 000),
M. The following line contains N integers describes the integer sequence. Every integer in the sequence is between 0 and 1 000 000 inclusively.

Output

Output one integer which is the minimum sum of the maximum integer of each part. If no such cuttings exist, output ?1.

Sample Input

8 17
2 2 2 8 1 8 2 1

Sample Output

12

把序列分成若干部分,每一部分的和不超过m,求每一部分里最大值和的最小值。

开始没啥思路,研究了半天,感觉单调队列dp非常的精妙,先mark一下,后面慢慢理解吧。

代码:

/* ***********************************************
Author :_rabbit
Created Time :2014/5/13 1:35:25
File Name :C.cpp
************************************************ */
#pragma comment(linker, "/STACK:102400000,102400000")
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <string>
#include <time.h>
#include <math.h>
#include <queue>
#include <stack>
#include <set>
#include <map>
using namespace std;
#define INF 0x3f3f3f3f
#define eps 1e-8
#define pi acos(-1.0)
typedef long long ll;
ll que[100100],a[100100],dp[100100];
int main()
{
     //freopen("data.in","r",stdin);
     //freopen("data.out","w",stdout);
	 //dp 方程:f[i]=f[j]+max(x[j+1],x[j+2],...,x[i]),其中j<i,x[j+1]+x[j+2]+...+x[i]<=m;
     ll n,m;
	 while(~scanf("%lld%lld",&n,&m)){
		 bool flag=1;
		 for(int i=1;i<=n;i++){
			 scanf("%lld",&a[i]);
			 if(a[i]>m)flag=0;
		 }
		 if(!flag){
			 puts("-1");continue;
		 }
		 ll front=0,rear=0,p=1;
		 dp[1]=a[1];que[rear++]=1;
		 ll sum=a[1];
		 for(ll i=2;i<=n;i++){
			 sum+=a[i];
			 while(sum>m)sum-=a[p++];//区间和小于等于m
			 while(front<rear&&a[i]>=a[que[rear-1]])rear--;//单调严格递减队列
			 que[rear++]=i;
			 while(que[front]<p&&front<rear)front++;//把远离的弹出。
			 dp[i]=dp[p-1]+a[que[front]];
			 for(ll j=front+1;j<rear;j++)
				 dp[i]=min(dp[i],dp[que[j-1]]+a[que[j]]);//枚举队列中的元素,求最优解。
		 }
		 cout<<dp[n]<<endl;
	 }
     return 0;
}

POJ 3017 单调队列dp,布布扣,bubuko.com

时间: 2024-10-19 21:22:21

POJ 3017 单调队列dp的相关文章

poj 3017 单调队列

只能说是代码美如画.(当然我是指内在逻辑不是我的代码风格,队长看到要理解哈,啊哈哈哈哈哈哈哈哈) 正常思路咯,f[i]=f[j]+max(a[j+1],a[j+2]....a[i]),枚举j,显然硬来会超时,所以需要有一个单调队列来时时把最大值尽快弄出来并且需要一些剪枝: 剪枝条件有两个,一个是和不能超过m,一个是显然f[i]是个非严格递增序列,根据这两个条件剪枝: 则建立单调队列,每当插入新的i时将前面先把和小于等于m的条件做好,然后对于j<i,如果a[j]<a[i]就可以把j丢弃,那么显然

[TyvjP1313] [NOIP2010初赛]烽火传递(单调队列 + DP)

传送门 就是个单调队列+DP嘛. ——代码 1 #include <cstdio> 2 3 const int MAXN = 1000001; 4 int n, m, h = 1, t = 1, ans = ~(1 << 31); 5 int q[MAXN], a[MAXN], f[MAXN]; 6 7 inline int min(int x, int y) 8 { 9 return x < y ? x : y; 10 } 11 12 int main() 13 { 14

hdu4374单调队列+dp

http://acm.hdu.edu.cn/showproblem.php?pid=4374 Problem Description Now there is a game called the new man down 100th floor. The rules of this game is: 1.  At first you are at the 1st floor. And the floor moves up. Of course you can choose which part

Sliding Window POJ - 2823 单调队列模板题

Sliding Window POJ - 2823 单调队列模板题 题意 给出一个数列 并且给出一个数m 问每个连续的m中的最小\最大值是多少,并输出 思路 使用单调队列来写,拿最小值来举例 要求区间最小值 就是维护一个单调递增的序列 对于样例 8 3 1 3 -1 -3 5 3 6 7 我们先模拟一遍 1.队列为空 1 进队 队列:1 2.3>队尾元素 3 进队 队列: 1 3 3.-1小于队尾元素,一直从尾部出队知道找到比-1小的元素或者队列为空 队列:-1 当队列中元素大于m的时候从队头删

UVA Live Achrive 4327 Parade (单调队列,dp)

容易想到dp[i][j]表示在第i行j个路口的开始走最大高兴值. 每次可以向左走,或者向右边走,然后向北走.(或者直接往北) 向左走到,状态转移为dp[i][j] = dp[i][k] + happy[i][k][j](为了方便处理,i从1开始编号,0行dp值存0) 处理出前缀和,happy[i][k][j]表示为sum[i][j] - sum[i][k] 向左走应该取max(dp[i][k]-sum[i][k]) k应该满足time[i][k][j] <= k 随着j的变化k的范围是一个滑动窗

BZOJ 1791 岛屿(环套树+单调队列DP)

题目实际上是求环套树森林中每个环套树的直径. 对于环套树的直径,可以先找到这个环套树上面的环.然后把环上的每一点都到达的外向树上的最远距离作为这个点的权值. 那么直径一定就是从环上的某个点开始,某个点结束的. 把环拆成链,定义dp[i]表示第i个点为结束点的最远距离,显然有dp[i]=val[j]+sum[i]-sum[j-1]+val[i].显然可以用单调队列优化这个DP. 剩下的就是这样依次统计每个环套树的直径之和. 对于环套树上找环可以借鉴最小树形图找环的技巧. 首先将边定向,保证每个点的

POJ 2838 单调队列

Sliding Window Time Limit: 12000MS   Memory Limit: 65536K Total Submissions: 55309   Accepted: 15911 Case Time Limit: 5000MS Description An array of size n ≤ 106 is given to you. There is a sliding window of size k which is moving from the very left

vijos P1243 生产产品(单调队列+DP)

P1243生产产品 描述 在经过一段时间的经营后,dd_engi的OI商店不满足于从别的供货商那里购买产 品放上货架,而要开始自己生产产品了!产品的生产需要M个步骤,每一个步骤都可以在N台机器中的任何一台完成,但生产的步骤必须严格按顺序执行.由于这N 台机器的性能不同,它们完成每一个步骤的所需时间也不同.机器i完成第j个步骤的时间为T[i,j].把半成品从一台机器上搬到另一台机器上也需要一定的 时间K.同时,为了保证安全和产品的质量,每台机器最多只能连续完成产品的L个步骤.也就是说,如果有一台机

BZOJ 3831 POI 2014 Little Bird 单调队列DP

题目大意:给出一片树林,树排成一排,每一棵树都有一个高度.从地一棵树出发,每次可以跳到i+k棵之前,跳到小于自己高度的树上不需要花费体力,反之需要花费一点体力,问到最后一棵树最少需要多少体力. 思路:简单DP方程:f[i] = min{f[j] + (height[i] >= height[j])} 然后发现数据范围只有O(n)可以过. 维护单调队列,队列中按照f单调递减,队尾按照时间往出弹. 当f值相同的时候,高度较高的优先. CODE: #include <queue> #inclu