zoukankan      html  css  js  c++  java
  • 【POJ3017】Cut the Sequence

    题目大意:给定一个长度为 N 的序列,将序列划分成若干段,保证每段之和不超过 M,问所有段的最大值之和最小是多少。

    题解:设 (f[i]) 表示前 i 个数满足上述条件的最优解,显然有状态转移方程$$f[i]=min{f[j]+max_{j+1le k le i}{a[k]}}$$,发现若能够在 (O(1)) 的时间内求得静态区间最小的 a 值,则时间复杂度为 (O(n^2))
    可以发现,这个算法复杂度的瓶颈是每次都需要枚举 j 来做状态转移,于是观察递推式的结构,由于 f[i] 表示的是每段的最大值之和,可知 f[i] 这个序列单调不减,同时,对于一段区间的最大值而言,可以有很多转移的方式,既然 f[i] 序列单调不减,则可以将决策直接定在能够符合条件的 j 的最小值即可。通过这样,将枚举 j 寻找决策点的情况转化成了对于每个区间最大值对应的区间的最前端进行决策。而每个区间最大值可以采用单调队列进行维护即可,而取哪个点进行转移的最终决策还是要通过比较大小才能够知道,在这里可以用平衡树进行维护。
    (QAQ看了好长时间才理解

    代码如下

    #include <cstdio>
    #include <set>
    using namespace std;
    const int maxn=1e5+10;
    
    int n,a[maxn],q[maxn];
    long long m,sum[maxn],f[maxn];
    multiset<long long> s;
    
    int main(){
    	scanf("%d%lld",&n,&m);
    	for(int i=1;i<=n;i++){
    		scanf("%d",&a[i]);
    		if(a[i]>m){puts("-1");return 0;} 
    		sum[i]=sum[i-1]+a[i];
    	}
    	int l=1,r=0,j=0;
    	for(int i=1;i<=n;i++){
    		while(sum[i]-sum[j]>m)++j;
    		while(l<=r&&q[l]<=j){
    			if(l<r)s.erase(a[q[l+1]]+f[q[l]]);
    			++l;
    		}
    		while(l<=r&&a[q[r]]<=a[i]){
    			if(l<r)s.erase(a[q[r]]+f[q[r-1]]);
    			--r;
    		}
    		q[++r]=i;
    		if(l<r)s.insert(a[i]+f[q[r-1]]);
    		f[i]=f[j]+a[q[l]];
    		if(l<r)f[i]=min(f[i],*s.begin());
    	}
    	printf("%lld
    ",f[n]);
    	return 0;
    }
    
  • 相关阅读:
    八数码(BFS)
    食物链(并查集)
    最大异或对(Trie)
    解决espeak编译的一些问题
    AcWing 3779. 相等的和
    AcWing 3775. 数组补全(环图)
    AcWing 3728. 城市通电(最小生成树)
    AcWing 3727. 乘方相乘(进位制)
    tarjan
    LC刷题简要记录
  • 原文地址:https://www.cnblogs.com/wzj-xhjbk/p/10474463.html
Copyright © 2011-2022 走看看