【题解】poj1738石子合并 区间DP 加西亚瓦克斯算法

题目链接
乍一看很激动(诶辣鸡题才做过)然后n=4e4+o(n^3)=GG
GarsiaWachs算法
或者四边形优化(还是GG不用搞了)(以后自己写一遍)
还可以加上个平衡树(憋说了……)
step 0:初始数组为num[1..n],num[0] = num[n+1] = INF
step 1:每次找到一个最小的i使得num[i-1]<=num[i+1],将num[i-1]和num[i]合并为temp
step 2:找到前面一个最大的j使得num[j]>temp,将temp放在j之后。
step 3:重复1,2,直到剩余的堆数为1。
因为每次step2之后,指向的位置只需要向前一个即可(前面其他的都不会受到此次更新的影响),因此每次指针的移动并不多。也因此,一个理论复杂度其实有O(N^2)的算法能够轻松过掉这道题。

//GarsiaWachs
/*设序列是stone[],从左往右,找一个满足stone[k-1] <= stone[k+1]的k,
找到后合并stone[k]和stone[k-1],再从当前位置开始向左找最大的j,
使其满足stone[j] > stone[k]+stone[k-1],插到j的后面就行。
一直重复,直到只剩下一堆石子就可以了。
在这个过程中,可以假设stone[-1]和stone[n]是正无穷的。*/
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int N=5e4+5;
int n,t,ans,stone[N];
void combine(int k)
{
    int tmp=stone[k]+stone[k-1];//合并 
    ans+=tmp;
    for(int i=k;i<t-1;i++)
    stone[i]=stone[i+1];//左移 
    t--;
    int j=0;
    for(j=k-1;j>0&&stone[j-1]<tmp;j--)
    stone[j]=stone[j-1];
    stone[j]=tmp;//插入到j后面 
    while(j>=2&&stone[j]>=stone[j-2])
    {
        int d=t-j;
        combine(j-1);
        j=t-d;
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    while(scanf("%d",&n)!=EOF&&n)
    {
        for(int i=0;i<n;i++)
        scanf("%d",stone+i);
        t=1;
        ans=0;
        for(int i=1;i<n;i++)
        {
            stone[t++]=stone[i];
            while(t>=3&&stone[t-3]<=stone[t-1])
            combine(t-2);
        }
        while(t>1)combine(t-1);
        printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41958841/article/details/81636450
今日推荐