码迷,mamicode.com
首页 > 其他好文 > 详细

POJ 3017 Cut the Sequence

时间:2020-03-30 11:21:08      阅读:60      评论:0      收藏:0      [点我收藏+]

标签:mat   int   --   cpp   names   i++   iter   poj   一个   

https://vjudge.net/problem/POJ-3017

题目

给一个长度为$N$的序列,你需要把它切成几段,每一段的和不能超过$M$,求一种切法,使每一段的最大值的和最小。

$N\leqslant100000$,$M$不会爆long long,序列中的数在$[0,1000000]$

题解

$dp[i]=\min\{dp[j]+\max\{a[j+1],\cdots,a[i]\} | s[i]-s[j]\leqslant M\}$

时间复杂度$\mathcal{O}(n^2)$

假设最优的转移是$j$,那么想必要条件

$dp[j]+\max\{a[j+1],\cdots,a[i]\}<dp[j+1]+\max\{a[j+2],\cdots,a[i]\}$

因为$dp[i]$单调递增,所以必要条件是$j==N$或$a[j+1]\leqslant\max\{a[j+2],\cdots,a[i]\}$即$a[j+1]\ne\max\{a[j+1],\cdots,a[i]\}$

$dp[j]+\max\{a[j+1],\cdots,a[i]\}<dp[j-1]+\max\{a[j],\cdots,a[i]\}$

必要条件是$s[i]-s[j-1]>M$或$a[j]\geqslant\max\{a[j+1],\cdots,a[i]\}$,即$a[j]=\max\{a[j],\cdots,a[i]\}$

第二个必要条件好用一些,可以利用单调队列优化,可以在$\mathcal{O}(1)$时间得到每个$i$必要的决策

由于要求值最小的决策,可以使用multiset,时间复杂度$\mathcal{O}(n\log n)$

AC代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<set>
#define REP(i,a,b) for(register int i=(a); i<(b); i++)
#define REPE(i,a,b) for(register int i=(a); i<=(b); i++)
#define PERE(i,a,b) for(register int i=(a); i>=(b); i--)
using namespace std;
typedef long long ll;
#define MAXN 100007
int N; ll M;
int a[MAXN];
ll dp[MAXN];
int ma[MAXN];
multiset<ll> ms;
typedef multiset<ll>::iterator msi;
int main() {
	scanf("%d%lld", &N, &M);
	REPE(i,1,N) {
		scanf("%d", &a[i]);
		if(a[i]>M) {puts("-1"); return 0;}
	}
	dp[0]=0;
	int l1=1,l2=0,r2=0; ll ss=0;
	REPE(i,1,N) {
		ss+=a[i];
		while(ss>M) ss-=a[l1++]; //l1<i
		while(l2<r2 && ma[l2]<l1) {
			int x=ma[l2];
			if(l2+1<r2) {
				msi it=ms.find(dp[x]+a[ma[l2+1]]);
				if(it!=ms.end()) ms.erase(it);
			}
			l2++;
		}
		while(l2<r2 && a[ma[r2-1]]<a[i]) {
			int x=ma[r2-1];
			if(r2-2>=l2) {
				msi it=ms.find(dp[ma[r2-2]]+a[x]);
				if(it!=ms.end()) ms.erase(it);
			}
			r2--;
		}
		if(r2>l2) {
			ms.insert(dp[ma[r2-1]]+a[i]);
		}
		ma[r2++]=i;
		
		dp[i]=dp[l1-1]+a[ma[l2]];
		if(!ms.empty()) {
			dp[i]=min(dp[i], *ms.begin());
		}
	}
	printf("%lld\n", dp[N]);
	
}

 

POJ 3017 Cut the Sequence

标签:mat   int   --   cpp   names   i++   iter   poj   一个   

原文地址:https://www.cnblogs.com/sahdsg/p/12597237.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!