Cut the Sequence(POJ3017)(BSOJ1247)

poj3017

题目大意:给一个长度为n的序列a,要你将其分成若干段,在满足每段和不超过m的情况下,让每段中的最大值之和最小,求最小值。

容易想到动态规划,令f[i]表示把前i个数分段所得的最小值

f[i]=min{f[j]+max (j+1<=k<=i){a[k]}}    (a[j+1]+a[j+2]+......+a[i]<=m

时间复杂度o(n2),我们考虑如何优化。

考虑单调队列,由于f[i]一定是单调不降的,

对于任意两个连续决策j-1,j(0<=j-1<j<i且a[j+1]+a[j+2]+......+a[i]<=m),我们分两种情况:

情况1:(a[j]+a[j+2]+......+a[i]>m

              此时j-1不符合条件,j有用,优

情况2:若(a[j]+a[j+2]+......+a[i]<=m

             当 存在  f[j-1]+max(j<=k<=i){a[k]}  <=   f[j]+max(j+1<=k<=i){a[k]}

             说明j-1更优,且随着i的增大,j-1更容易满足小于i,所以j为无用决策,应删除。


             所以当且仅当    f[j-1]+max(j<=k<=i){a[k]}  > f[j]+max(j+1<=k<=i){a[k]}  ,j有用.

             因为f[j-1]<=f[j]

              所以    max(j<=k<=i){a[k]}  > max(j+1<=k<=i){a[k]}

              所以   a[j]=max(j<=k<=i){a[k]}

综上:若j更优,除了满足(a[j+1]+a[j+2]+......+a[i]<=m)

           还应当满足两个条件之一

                条件一   a[j]+a[j+2]+......+a[i]>m

               条件二   a[j]=max(j<=k<=i){a[k]}

条件一容易处理,考虑条件二,维护一个决策点j单调递增,a[j]单调递减的队列即可。

至于max (j+1<=k<=i){a[k]}如何得到,其实就是队列下一个元素的a值。

但注意到维护的队列对f[j]+max (j+1<=k<=i){a[k]}没有单调性,所以建立一个数据结构,如平衡树set,存f[j]+max (j+1<=k<=i){a[k]},

以便快速得到。(数据太水,直接暴力扫队列竟然还要快一些)

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<set>
 6 #define ll long long
 7 using namespace std;
 8 int f[100005],a[100005],q[100005];
 9 int n;
10 ll m,sum[100005];
11 multiset<int> s;
12 int main()
13 {
14     int i,j;
15     scanf("%d%lld",&n,&m);
16     int L=1,r=0,k=0;//k为队列区间起始位置L的前一个位置 (维护条件2)
17     for(i=1;i<=n;i++)scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
18     for(i=1;i<=n;i++)
19     {
20         if(a[i]>m){printf("-1");return 0;}
21         while(sum[i]-sum[k]>m)k++;//维护k 
22         while(L<=r&&q[L]<=k)
23         {
24             if(L<r)s.erase(f[q[L]]+a[q[L+1]]);//删去 区间和大于m的候选项 
25             L++;
26         }
27         while(L<=r&&a[q[r]]<=a[i])
28         {
29             if(L<r)s.erase(f[q[r-1]]+a[q[r]]);//维护a[]单调递减 
30             r--;
31         }
32         q[++r]=i;
33         if(L<r)s.insert(f[q[r-1]]+a[i]);
34         f[i]=f[k]+a[q[L]];//条件1更新f 
35         if(L<r)f[i]=min(f[i],*s.begin());//条件2更新f 
36     }
37     printf("%d",f[n]);
38     return 0;
39 }

猜你喜欢

转载自www.cnblogs.com/dsb-y/p/11008366.html
cut