#GarsiaWachs#poj 1738 codevs 2102 codevs 3002 codevs 2298 石子合并

题目

有n堆石子排成一列,每堆石子有一个重量w[i], 每次合并可以合并相邻的两堆石子,一次合并的代价为两堆石子的重量和w[i]+w[i+1]。问安排怎样的合并顺序,能够使得总合并代价达到最小。


分析

为什么这次不写codevs 1048呢?
O ( n 3 ) n 3000 n 50000 根本无能为力,
所以要介绍一种新的方法
非动态规划GarsiaWachs
动态规划也可以做,但是我不会(用到四边形不等式)

设一个序列是A[0..n-1],每次寻找最小的一个满足A[k-1]<=A[k+1]的k,那么我们就把A[k]与A[k-1]合并,之后从k向前寻找第一个满足A[j]>A[k]+A[k-1]的j,把合并后的值A[k]+A[k-1]插入A[j]的后面。
神奇地过了, O n 2 (用平衡树优化 O ( n l o g n ) ,可是我不会)


代码

#include <cstdio>
using namespace std;
int a[50001],t,ans,n;
int in(){
    int ans=0; char c=getchar();
    while (c<48||c>57) c=getchar();
    while (c>47&&c<58) ans=ans*10+c-48,c=getchar();
    return ans;
}
void comb(int k){
    int tmp=a[k]+a[k-1],d; ans+=tmp; t--;
    for (int i=k;i<t;i++) a[i]=a[i+1];
    int j=0;
    for (j=k-1;j&&a[j-1]<tmp;j--) a[j]=a[j-1];
    a[j]=tmp;
    while (j>=2&&a[j]>=a[j-2]) d=t-j,comb(j-1),j=t-d;
}
int main(){
    while (1){
        n=in();
        if (!n) return 0;
        for (int i=0;i<n;i++) a[i]=in();
        t=1; ans=0;
        for (int i=1;i<n;i++){
            a[t++]=a[i];
            while (t>=3&&a[t-3]<=a[t-1]) comb(t-2);
        }
        while (t>1) comb(t-1);
        printf("%d\n",ans);
    }
}

猜你喜欢

转载自blog.csdn.net/sugar_free_mint/article/details/81705984