您的位置:首页 > 产品设计 > UI/UE

POJ Cut the Sequence 单调队列优化DP入门题

2012-05-23 22:49 423 查看
Cut the Sequence

Time Limit: 2000MSMemory Limit: 131072K
Total Submissions: 6478Accepted: 1780
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

Hint

Use 64-bit integer type to hold M.

Source
POJ Monthly--2006.09.29, zhucheng

/*
* File:   main.cpp
* Author: hit-acm
*
* Created on 2012年5月22日, 下午11:44
*/

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
#define MIN(A,B) (A<B)?A:B
/*
* 此题的状态转移方程是很容易写的出来的,dp[i]=min(dp[j)+max(num[j+1->i])]),(sum(num[j+1->i])<=M);
* 很明显是个n^2的DP,看一下数据范围必然超时,我们不得不想一下如何去优化此dp,使其复杂度变为至少是
* nlgn级别的,如何去优化成为了一个难题。以我原来的知识是没有任何思路的。现在我能想的就是学习一种新
* 的思想,就是用单调队列优化。看了很多神人的讲解之后,以此题作为入门学习的题目来进行学习。
* 我们拿输入來举例:
* 8 17
* 2 2 2 8 1 8 2 1
* 单调队列的变化形式为:
* 第一个元素插入后:2
* 第二个元素插入后:2
* 第三个元素插入后:2
* 第四个元素插入后:8
* 第五个元素插入后:8 1
* 第六个元素插入后:8
* 第七个元素插入后:8 2
* 第八个元素插入后:8 2 1
*
* 那么如何优化呢?
* 现在我们先证明一个结论:
* 有x<y,必然有:dp[x]<=dp[y]
*(对于这个是很容易理解的,对于y的任何合法分组,x都可以通过
* 构造,形成与其对应的分组。从而保证dp[x]<=dp[y]恒成立。
* )。那么如果我们不考虑任意段的和不超过M这个限制条件的话,
* 我们就可以利用dp[1]依次更新dp[3]一旦成功就可以直接break掉
* 而不必,全部都更新一次。
*
* 关键是代码如何实现呢?
* 我们可以设置一个st代表恰好满足:
* sum[st,i]<=M.同时对单调队列进行更新,使过时的区间最大值消失
* 那么用:dp[i]=dp[st-1]+当前单调队列的最大值,来更新但前的DP
* 那么这就是结果么?不是的。比如4 5 3 M为 10.这种情况
* dp[1]=4, dp[2]=5,dp[3]=dp[2]+3=8,如果按照上式就是:
* dp[3]=dp[1]+5=9.
* 为什么会出现这种情况呢?
* 实际上对于单调队列中的情况如:
* 8 2 1时。
* 我们可以保证的是:
* 8-2 ,2-1之间的满足。
* 当x<y时,dp[x]+当前队列的最大值<=dp[y]+当前队列的最大值
* 我们已经证明了:当x<y时,dp[x]<dp[y];
* 又由于当前队列的最大值是相同的,因此不等式满足。
* 因此,我们需要把单调队列里的所有备选方案全部拿出来对结果
* 进行更新,那么得出的就是最优解。
*/

const int MAX = 100011;
long long a[MAX];
long long dp[MAX];
long long sum[MAX];
int p[MAX];
int start, end;

void insert(int x) {
if (start > end) {
start = end = 1;
p[start] = x;
return;
}
while (start <= end && a[p[end]] <= a[x]) end--;
p[++end] = x;
}

void update(int st) {
while (start <= end && st > p[start]) start++;
}

void init() {
start = 1;
end = 0;
}

int main() {
int n;
long long M;
while (scanf("%d%lld", &n, &M) == 2) {
init();
sum[0] = 0;
dp[0] = 0;
int st = 1;
bool flag = false;
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
if (!flag && a[i] > M) {
flag = true;
}
sum[i] = sum[i - 1] + a[i];
insert(i);
while (sum[i] - sum[st - 1] > M) {
st++;
}
update(st);
dp[i] = dp[st - 1] + a[p[start]];
for (int j = start; j < end; j++) {
dp[i] = MIN(dp[i], dp[p[j]] + a[p[j + 1]]);
}
}
if (flag) {
printf("-1\n");
} else {
printf("%lld\n", dp
);
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: