首页 > 其他 > 详细

洛谷2300 合并神犇

时间:2018-09-07 13:19:30      阅读:156      评论:0      收藏:0      [点我收藏+]

题目传送门


一句话题意


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


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];
}

洛谷2300 合并神犇

原文:https://www.cnblogs.com/Le-mon/p/9603628.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!