• 刷题总结——Cut the Sequence(POJ 3017 dp+单调队列+set)


    题目:

    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.

    题解:

    哇塞···这道题太bt了·····

    引用sdj222555题解:%%%%%%%

    令f[i] 表示前i个数按照题目要求的最小的和

    则必然有f[i] = min(f[j] + max(a[j +1 , a[j + 2].....a[i])) 

    其中j<= i,j的位置还得满足题目中m 的限制

    由于a数组都是大于0的,所以可以发现f必然是非递减的。

    设a[j + 1], a[j + 2], ...a[i]中值最大的下标为k

    设x为[j + 1,k]的任意一个下标,则a[x],a[x+1],....a[i]的最大值的下标显然也是k了

    由f的非递减性,f[j+1] + a[k] <= f[j+2]+a[k].....<= f[k - 1] + a[k] 

    很显然,我们只要取f[j+1]+a[k]就可以了。

    也就是说如果某一段到当前i位置的最大值都一样,取最靠前的即可。

    如何维护呢,可以联想到单调队列。

    维护一个递减的队列,存的是符合要求的某一段的最大值,但是可以发现,并不是队首元素就是最优,因为队列中的递减性质,队列中的所有元素都有可能导致最优解。

    这时可以用到的东西就很多了,堆啊,各种树这样的。实际上,用个set可以有效的减少代码量。

    那么为什么不用递增的队列呢? 如果用递增,比如队列中存的是a[1] a[2] a[3]  (a[1]  < a[2] < a[3]) ,现在的位置是x,那么a[1], a[2] a[3]到x位置的最大值是相等的。那么a[2]和a[3]就没有存在的意义了。然后就又变成了递减的序列。

    最后不得不说比较难想的地方在于a在单调队列中是递减的··这也是关键部分····

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<string>
    #include<algorithm>
    #include<cmath>
    #include<ctime>
    #include<cctype>
    #include<set>
    using namespace std;
    const int N=1e5+5;
    int num[N],que[N],le,ri,n,low,maxx=0;
    long long tot,m,f[N];
    multiset<int>st;
    int main()
    {
      //freopen("a.in","r",stdin);
      scanf("%d%I64d",&n,&m);  
      for(int i=1;i<=n;i++)  
      {  
        scanf("%d",&num[i]);
        if(num[i]>m)  {cout<<"-1"<<endl;return 0;}
      }
      low=1,le=1,ri=0;
      for(int i=1;i<=n;i++)
      {
        tot+=num[i];
        while(tot>m)  tot-=num[low++];
        while(le<=ri&&num[i]>=num[que[ri]])
        {
          if(ri>le)  st.erase(f[que[ri-1]]+num[que[ri]]);ri--;
        }
        que[++ri]=i;
        if(ri>le)  st.insert(f[que[ri-1]]+num[que[ri]]);
        while(que[le]<low)  
        {
          if(le<ri)  st.erase(f[que[le]]+num[que[le+1]]);le++;
        }
        int temp=*st.begin();f[i]=f[low-1]+num[que[le]];
        if(le<ri&&temp<f[i])  f[i]=temp;
      }
      cout<<f[n]<<endl;
      return 0;
    }
  • 相关阅读:
    VS2010 添加项目依赖
    人工鱼群算法 AFSA
    粒子群算法 PSO
    CUDA速度测试
    AGSO 萤火虫算法
    用于WTL工具栏的辅助类CToolBarHelper
    关于结构体内存对齐
    遗传算法 GA
    A*算法
    人工蜂群算法 ABC
  • 原文地址:https://www.cnblogs.com/AseanA/p/7647956.html
Copyright © 2020-2023  润新知