zoukankan      html  css  js  c++  java
  • POJ 3017 Cut the Sequence

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

    题目

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

    $Nleqslant100000$,$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]leqslantmax{a[j+2],cdots,a[i]}$即$a[j+1] emax{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]geqslantmax{a[j+1],cdots,a[i]}$,即$a[j]=max{a[j],cdots,a[i]}$

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

    由于要求值最小的决策,可以使用multiset,时间复杂度$mathcal{O}(nlog 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
    ", dp[N]);
    	
    }
    
  • 相关阅读:
    CSS3阴影 box-shadow的使用和技巧总结
    事件
    表单操作
    DOM
    BOM
    js总结1
    css3
    css图片文字相关属性
    CSS盒子模型及布局
    写博客的几个注意事项
  • 原文地址:https://www.cnblogs.com/sahdsg/p/12597237.html
Copyright © 2011-2022 走看看