GarsiaWachs算法

来源:互联网 发布:淘宝店铺没人买 编辑:程序博客网 时间:2024/05/23 19:19

石子合并
总时间限制: 1000ms 内存限制: 128000kB

描述
有N堆石子排成一排(n<=5000),现要将石子有次序地合并成一堆,规定每次只能选相邻的两堆合并成一堆,并将新的一堆的石子数,记为改次合并的得分,编一程序,由文件读入堆数n及每堆石子数(<=100);选择一种合并石子的方案,使得做n-1次合并,得分的总和最少

输入
第一行为石子堆数n
第二行为每堆石子数,每两个数之间用一空格分隔。

输出
输出为一行,为最小的合并得分

样例输入
4
4 5 9 4

样例输出
44

GarsiaWachs算法:专用于石子合并问题。它将时间复杂度从划分DP的O(n^3)降到了O(n^2)。

定义一个石子序列A[0], A[1], A[2], ..., A[n-1],则计算这个石子序列的最小合并数ans的算法如下:
1.(假设存在A[-1]A[n],将它们的值设为无穷大。这个假设只是便于理解,在代码上不用这么写)
2.从-1到n, 找到第一个k使A[k]<=A[k+2],保存这个k
3.定义一个临时变量temp,使temp=A[k]+A[k+1],同时ans+=temp
4.从k-1到-1,找到第一个i使A[i]>temp,保存这个i
5.从表A中同时删去A[k]A[k+1](注意:这个操作不会影响A[0]A[k-1]的部分)
6.在i之后插入一个temp
7.如此,把2~6共循环n-1次,最后ans即为答案

参考代码

#include <cstdio>#include <cstdlib>#include <cmath>#include <cstring>#include <string>#include <iostream>#include <algorithm>#include <vector>#include <stack>#include <queue>#include <deque>#include <map>#include <set>using std::cin;using std::cout;using std::endl;inline int readIn(){    int a;    scanf("%d",&a);    return a;}int n;long long ans;std::vector<int> L;int Combine(){    int k=L.size()-1-1;    //如果我们在A[0]到A[n-3]找不到A[k]<=A[k+2],那么k的值应该为n-2,即倒数第二个元素。因为我们假设A[n]=+∞    for(int i=0; i<L.size()-2; i++) //因为要+2,所以在0到n-3中找    {        if(L[i]<=L[i+2])        {            k=i;            break; //找第一个k        }    }    int ret=L[k]+L[k+1]; //前文的temp    L.erase(L.begin()+k);    L.erase(L.begin()+k); //由于数组已经向左移了一个了,因此之前的A[k+1]跑到了A[k]的位置上,所以还是删除A[k]    int index=-1; //如果在A[k-1]到A[0]找不到A[i]>temp,我们就在A[-1]后插入temp,因为A[-1]=+∞    for(int i=k-1; i>=0; i--) //从右往左    {        if(L[i]>ret)        {            index=i;            break; //第一个        }    }    L.insert(L.begin()+index+1, ret); //因为是在后面插入,所以要+1    return ret; //加上这个返回值}int main(){    scanf("%d",&n);    for(int i=1; i<=n; i++)    {        L.push_back(readIn());    }    for(int i=0; i<n-1; i++)    {        ans+=Combine();    }    printf("%lld",ans);    return 0;}
原创粉丝点击