• BZOJ 3229: [Sdoi2008]石子合并


    3229: [Sdoi2008]石子合并

    时间限制: 3 Sec  内存限制: 128 MB
    提交: 497  解决: 240
    [提交][][]

    题目描述

      在一个操场上摆放着一排N堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。
      试设计一个算法,计算出将N堆石子合并成一堆的最小得分。
     

    输入

      第一行是一个数N。
      以下N行每行一个数A,表示石子数目。
     

    输出

      共一个数,即N堆石子合并成一堆的最小得分。

     

    样例输入

    4
    1
    1
    1
    1

    样例输出

    8

    提示

    对于 100% 的数据,1≤N≤40000

    对于 100% 的数据,1≤A≤200


    接下来是嘴巴时间!!



    po1:
      显然如果数据范围变小这可是一道DP入门题:f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+sum(i,j)) (i,j表示的是区间[i,j]的最优解,i<=k<j)
      O(N^3)!
     
    po2:
      为了降低复杂度
      介绍四边形优化
      设s(i,j)为f[i][j]为最优解时k的值,假设我们已经知道四边形优化是这样的s(i,j-1)<=s(i,j)<=s(i+1,j){我是不会证明的}那么在循环k的时候我们就只用从s(i,j-1)循环到(s+1,j)了,这是近乎于O(1)的。
      O(N^2)!! 但是数组无法滚动会卡掉空间QAQ
     
    po3:
      GarsiaWachs!
      这是专门用于解决石子合并类问题的算法:一个序列是A[0..n-1],每次寻找最小的一个满足A[k-1]<=A[k+1]的k,(方便起见设A[-1]和A[n]等于正无穷大)那么我们就把A[k]与A[k-1]合并,之后找最大的一个满足A[j]>A[k]+A[k-1]的j,把合并后的值A[k]+A[k-1]插入A[j]的后面,反复进行直到序列为1个数字。
      栗子:
      186 64 35 32 103  (35<103)
      186 67 64 103  (64<103) 
      186 131 103   (A[-1]和A[n]等于正无穷大)
      234 186
      420
      ans=420+234+131+67=852
      O(N^2) 可以勉强过了这道题
     
    po4:
       在po3的基础上splay维护此序列。
      O(NlogN)

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstdlib>
     4 #include<cmath>
     5 #include<algorithm>
     6 #define yyj(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout);
     7 #define llg long long
     8 #define maxn 40010
     9 llg i,j,k,x,n,m,a[maxn],ans;
    10 using namespace std;
    11 llg get()
    12 {
    13     llg i=0; char c=getchar();
    14     while(c>'9'||c<'0')c=getchar();
    15     while(c>='0'&&c<='9')i=i*10+c-'0',c=getchar();
    16     return i;
    17 }
    18 
    19 int main()
    20 {
    21     yyj("a");
    22     cin>>n;
    23     for (i=1;i<=n;i++) a[i]=get(); 
    24     a[0]=a[n+1]=0x7fffffff;
    25     for (m=1;m<n;m++)
    26         {
    27             a[n-m+2]=0x7fffffff;
    28             for (k=2;k<=n-m+1;k++) if (a[k-1]<=a[k+1]) break;
    29             x=a[k-1]+a[k]; ans+=x;
    30             for (i=k-1;i<=n-m;i++) a[i]=a[i+2];
    31             for (j=k-2;j>=0;j--) if (a[j]>x) break;
    32             for (i=n-m;i>j+1;i--) a[i]=a[i-1];
    33             a[j+1]=x;
    34         }
    35     cout<<ans;
    36     return 0;
    37 }
    View Code

    你以为这可以A?这只是一发常数写大了超时的

                                               当你把常数写小

    1548112 xrdog 3229 正确 1484 kb 60 ms C++/Edit 1080 B 2016-07-14 20:13:13
    本文作者:xrdog 作者博客:http://www.cnblogs.com/Dragon-Light/ 转载请注明出处,侵权必究,保留最终解释权!
  • 相关阅读:
    学习Python第五天
    学习Python第三天
    Mongodb
    JMS links
    笔记整理
    a* products
    String
    business knowledge
    [Java coding] leetcode notes
    [Java Basics3] XML, Unit testing
  • 原文地址:https://www.cnblogs.com/Dragon-Light/p/5671762.html
Copyright © 2020-2023  润新知