• 石子合并(NOI1995)


    石子合并(NOI1995)

    时间限制: 1 Sec  内存限制: 128 MB
    提交: 90  解决: 48
    [提交][状态][讨论版]

    题目描述

    在操场上沿一直线排列着 n堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的两堆石子合并成新的一堆, 并将新的一堆石子数记为该次合并的得分。允许在第一次合并前对调一次相邻两堆石子的次序。 
    计算在上述条件下将n堆石子合并成一堆的最小得分和初次交换的位置。 

    输入

    输入数据共有二行,其中,第1行是石子堆数n≤100; 
    第2行是顺序排列的各堆石子数(≤20),每两个数之间用空格分隔。 

    输出

    输出合并的最小得分。

    样例输入

    3
    2 5 1
    

    样例输出

    11
    
    看题目可以看出它是没有环的,貌似有环的石子归并也有。题目中开始可以交换相邻的两堆石子可以用枚举实现,然后就是2D/1D类型的dp
    一次复杂度为O(n^3),所有总复杂度为O(n^4),这是复杂度一算为1*10^8,也就是100 million,这个还需要卡常数,在当时绝对是卡不过去的,
    这时一个非常强势的优化就出现了,那就是平行四边形优化,可以缩掉一维转移的时间。

    【定理 1】假如函数 w 满足上述条件,那么函数 m 也满足四边形不等式,即

    m (i, j ) + m (i’, j') £ m (i', j ) + m (i , j')  i ≤ i' < j ≤ j'

    我们定义 s (i , j ) 为函数 m (i , j ) 对应的决策变量的最大值

    【定理 2】假如 m (i , j ) 满足四边形不等式,那么 s (i , j ) 单调,即:

    s (i, j ) ≤ s (i, j +1) ≤ s (i + 1, j +1)

    以上比较粗略,详细可以见

    动态规划加速原理之四边形不等式 华中师大一附中 赵爽

    看一下代码比较好

    #include<cstdio> 
    #include<algorithm> 
    #include<cmath> 
    #include<cstring> 
    #include<string> 
    #include<iostream> 
      
    using namespace std; 
    const int MAXN=107; 
    int n,ans,dp[MAXN][MAXN],a[MAXN],s[MAXN][MAXN],sum[MAXN]; 
      
    void solve() 
    { 
        memset(dp,0,sizeof(dp)); 
        memset(s,0,sizeof(s)); 
        for (int i=1;i<=n;i++) 
            { 
                sum[i]=a[i]+sum[i-1]; 
                dp[i][i]=0; 
                s[i][i]=i; 
            } 
        for (int t=1;t<n;t++) 
            for (int i=1;i<=n-t;i++) 
                { 
                    int j=i+t; 
                    dp[i][j]=MAXN*MAXN*MAXN; 
                    for (int k=s[i][j-1];k<=s[i+1][j];k++) //这里s即为平行四边形优化的数组
                    { 
                        if (dp[i][j]>dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]) 
                        { 
                            dp[i][j]=dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]; 
                            s[i][j]=k; //记录下i,j的最优转移点。
                        } 
                    } 
                } 
        ans=min(ans,dp[1][n]);       
    } 
    int main() 
    { 
        scanf("%d",&n); 
        for (int i=1;i<=n;i++) 
        { 
            scanf("%d",&a[i]); 
        } 
        ans=MAXN*MAXN*MAXN; 
        solve(); 
        for (int i=1;i<n;i++) 
        { 
            swap(a[i],a[i+1]); 
            solve(); 
            swap(a[i],a[i+1]); 
        } 
        cout<<ans<<endl; 
    } 
  • 相关阅读:
    这一年来
    网络流复习笔记
    Codeforces Round #431
    Codeforces Round #398 (Div. 2)
    Codeforces Round #418 (Div. 2)
    【Codeforces 98E】 Help Shrek and Donkey 游戏策略神题
    【bzoj1878】[SDOI2009]HH的项链
    Round 403 div. 2
    Codeforces Round #417 (Div. 2)
    Codeforces Round #416 (Div. 2)
  • 原文地址:https://www.cnblogs.com/fengzhiyuan/p/6890878.html
Copyright © 2020-2023  润新知