• GarsiaWachs算法


    石子合并(每次合并相邻的两堆石子,代价为这两堆石子的重量和,把一排石子合并为一堆,求最小代价)
    是一个经典的问题。dp可以做到O(n*n)的时间复杂度,方法是:
    设f[i,j]为合并从i到j的石子所用最小代价。
    f[i,j]=min(sum(i,j)+f[i,k]+f[k+1,j])对所有i<=k<j,其中sum(i,j)表示从i到j的石子重量之和。
    设上式取等时k的值为w[i,j],有神牛证明过:w[i,j]>=w[i,j-1],w[i,j]<=w[i+1,j]
    这样,枚举k的时候,就有了一个上下界,从而搞掉了一维。
    
    而GarsiaWachs算法可以把时间复杂度压缩到O(nlogn)。
    具体的算法及证明可以参见《The Art of Computer Programming》第3卷6.2.2节Algorithm G和Lemma W,Lemma X,Lemma Y,Lemma Z。
    只能说一个概要吧:
    设一个序列是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]的后面。
    有定理保证,如此操作后问题的答案不会改变。
    举个例子:
    186 64 35 32 103
    因为35<103,所以最小的k是3,我们先把35和32删除,得到他们的和67,并向前寻找一个第一个超过67的数,把67插入到他后面
    186 64(k=3,A[3]与A[2]都被删除了) 103
    186 67(遇到了从右向左第一个比67大的数,我们把67插入到他后面) 64 103
    186 67 64 103 (有定理保证这个序列的答案加上67就等于原序列的答案)
    现在由5个数变为4个数了,继续!
    186 (k=2,67和64被删除了)103
    186 131(就插入在这里) 103
    186 131 103
    现在k=2(别忘了,设A[-1]和A[n]等于正无穷大)
    234 186
    420
    最后的答案呢?就是各次合并的重量之和呗。420+234+131+67=852。

    题目:

     代码:(借鉴的)

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    using namespace std;
    const int maxn=50005;
    const int inf=0x7fffffff;//2147483647
    int n,m,t,ans,stone[maxn];
    void dfs(int k){
        int tmp=stone[k-1]+stone[k];
        ans+=tmp;t--;
        for(int i=k;i<t;++i)stone[i]=stone[i+1];//元素左移,表示删掉了一个元素
        int j=0;k--;
        for(j=k;stone[j-1]<tmp;--j)stone[j]=stone[j-1];//元素右移,找到第一个满足条件的j
        stone[j]=tmp;//将tmp插到j后面
        while(j>=3&&stone[j-2]<=stone[j]){//继续向前查找是否还有满足条件的情况
            int d=t-j;//保存当前t离操作点的距离d
            dfs(j-1);//合并第j-1堆和第j-2堆石子
            j=t-d;//设置新的操作点j
        }
    }
    int main(){
        while(~scanf("%d",&n)&&n){
            for(int i=1;i<=n;++i)scanf("%d",&stone[i]);
            t=2,ans=0;stone[0]=stone[n+1]=inf;
            for(int i=2;i<=n;++i){
                stone[t++]=stone[i];
                while(t>3&&stone[t-3]<=stone[t-1])dfs(t-2);//表示当前至少有3堆石子,并且满足stone[k-1]<=stone[k+1],k=t-2,就合并第t-3和第t-2堆石子
            }
            while(t>2)dfs(t-1);//如果剩下的堆数至少为3-1=2堆,则继续合并,直至剩下一堆石子
            printf("%d
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    javascript:浮动div,可拖拽div,遮罩层(div和iframe实现)
    c#委托初探
    工业化生产:简单工厂、工厂方法和抽象工厂模式
    javascript:面向对象编程基础:多态
    sql server:自定义函数初探
    ajax:数据传输方式简介
    javascript:对cookie的基本操作
    javascript:内置对象学习笔记一
    保证一个类仅有一个实例:单例模式
    ajax:简单搜索实践篇
  • 原文地址:https://www.cnblogs.com/lusiqi/p/12373120.html
Copyright © 2020-2023  润新知