zoukankan      html  css  js  c++  java
  • 洛谷2300 合并神犇

    题目传送门

    一句话题意

    给你一个数列,每次可以把相邻两个数合并,求把这个数列变成不下降序列最少需要的操作次数。

    Solution

    因为洛谷数据比较水,所以这个题目有两种做法:
    1.(O(n^2)):直接DP,f[i]表示前 i 个数最少合并的次数。g[i]表示前 i 个数在满足合并了f[i]次的条件下最后一组的总和最小是多少,sum[i]是前缀和。
    转移的话直接枚举i是从前面哪一次转移过来就行了,(n^2)过2e5是真的骚,但是由于数列随机生成,几个数之和很容易会超过g[j],所以差不多第二重循环只会循环几次,差不多相当于常数,详情请看代码。
    2.(O(n)):这种方法需要使用单调队列因为很明显上一种方法中g数组具有单调性,所以可以用单调队列维护。

    Coding

    (O(n^2)):

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 1e6;
    long long p[N],f[N],sum[N],Min[N];
    int main()
    {
        int n;
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&p[i]);
            sum[i]=sum[i-1]+p[i];
        }
        for(int i=1;i<=n;i++)
        {
            int j;
        	for(j=i-1;j>=0;j--)
        		if(sum[i]-sum[j]>=Min[j]) break;
        		f[i]=f[j]+i-j-1;
        		Min[i]=sum[i]-sum[j];
        }
        cout<<f[n];
        return 0;
    }
    

    (O(n)):

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long lol;
    lol sum[1000001],pre[1000001],f[1000001];
    int q[1000001],head,tail,n;
    int main()
    {int i;
      cin>>n;
      for (i=1;i<=n;i++)
        {
          scanf("%lld",&sum[i]);
          sum[i]+=sum[i-1];
        }
      head=0;tail=1;
      q[0]=0;
      for (i=1;i<=n;i++)
        {
          while (head+1<tail&&sum[i]>=sum[q[head+1]]+pre[q[head+1]]) head++;
          f[i]=f[q[head]]+1;
          pre[i]=sum[i]-sum[q[head]];
          //cout<<q[head]<<endl;
          while (head<tail&&sum[q[tail-1]]+pre[q[tail-1]]>sum[i]+pre[i]) tail--;
          q[tail++]=i;
        }
      cout<<n-f[n];
    }
    
  • 相关阅读:
    Node.js安装及环境配置(windows)
    table
    检测浏览器
    ickeck插件
    全国三级联动
    css3-calc用法
    jQuery Portamento 滑动定位
    canvas版《俄罗斯方块》
    canvas入门级小游戏《开关灯》思路讲解
    css3 matrix 2D矩阵和canvas transform 2D矩阵
  • 原文地址:https://www.cnblogs.com/Le-mon/p/9603628.html
Copyright © 2011-2022 走看看