zoukankan      html  css  js  c++  java
  • 石子合并

    石子合并

       有N堆石子排成一排,每堆石子有一定的数量。现要将N堆石子并成为一堆。合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这两堆石子的和,经过N-1次合并后成为一堆。求出总的代价最小值。


      石子合并是一道十分经典的问题。注意到石子的合并可以当做区间的合并,显而易见的,我们有如下状态转移方程:

      

      程序也十分简单:

    #include<bits/stdc++.h>
    using namespace std;
    #define MAXN 220
    const int INF=0x3f3f3f3f;
    int n,w[MAXN],sum[MAXN],dp[MAXN][MAXN];
    int main(){
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&w[i]);
            sum[i]=sum[i-1]+w[i];
        }
        for(int i=n;i>=1;i--){
            for(int j=i+1;j<=n;j++){
                int tmp=INF;
                for(int k=i;k<j;k++)
                    tmp=min(tmp,dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);
                dp[i][j]=tmp;
            }
        }
        printf("%d
    ",dp[1][n]);
        return 0;
    }

      以上的算法正确性是可以保证的,但时间复杂度达到了O(n3),当n≥1000时,这样的时间复杂度是无法接受的。


      考虑进一步优化。

      回顾它的转移方程:

      

      根据平行四边形不等式,我们可以从dp[i][j-1]的决策点dp[i+1][j]的决策点枚举,时间复杂度是O(n2)

      

      代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    #define MAXN 5000+10
    typedef long long LL;
    const LL INF=0xffffff;
    int n,g[MAXN][MAXN];
    LL a[MAXN],sum[MAXN],f[MAXN][MAXN];
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                f[i][j]=INF;
        for(int i=1;i<=n;i++){
            sum[i]=sum[i-1]+a[i];
            f[i][i]=0;
            g[i][i+1]=i;
        }
        for(int i=1;i<=n;i++)f[i][i+1]=sum[i+1]-sum[i-1];
        for(int i=n-2;i;i--)
            for(int j=i+2;j<=n;j++)
                for(int k=g[i][j-1];k<=g[i+1][j];k++){
                    int tmp=f[i][k]+f[k+1][j]+sum[j]-sum[i-1];
                    if(f[i][j]>tmp){
                        f[i][j]=tmp;
                        g[i][j]=k;
                    }
                }
        printf("%lld",f[1][n]);        
        return 0;
    }

      尽管有了进一步的优化,但对于更大的数据,此算法也无能为力。(Codevs 2298

      


      实际上对于石子归并问题还有另一种算法:GarsiaWachs算法

      算法的大致流程如下:

        1.设一个序列是A[0..n-1],每次寻找最小的一个满足A[k-1]<=A[k+1]的k,把A[k]与A[k-1]合并

        2.之后找最大的一个满足A[j]>A[k]+A[k-1]的j,把合并后的值A[k]+A[k-1]插入A[j]的后面。

      由于此算法在其他题目中较少用到,在此不多赘述。可以证明,它的时间复杂度是O(nlogn)

      

      代码如下:  

    #include<bits/stdc++.h>
    using namespace std;
    #define MAXN 50000+10
    typedef long long LL;
    int t,n,a[MAXN];
    LL ans=0;
    void work(int k){
        int tmp=a[k-1]+a[k];
        ans+=tmp;
        for(int i=k;i<t-1;i++)a[i]=a[i+1];
        t--;
        int j=0;
        for(j=k-1;j>0&&a[j-1]<tmp;j--)a[j]=a[j-1];
        a[j]=tmp;
        while(j>=2&&a[j]>=a[j-2]){
            int d=t-j;
            work(j-1);
            j=t-d;
        }
    }
    int main(){
        scanf("%d",&n);
        ans=0;
        for(int i=0;i<n;i++)scanf("%d",&a[i]);
        t=1;
        for(int i=1;i<n;i++){
            a[t++]=a[i];
            while (t>=3&&a[t-3]<=a[t-1])
                work(t-2);
        }
        while(t>1)work(t-1);
        printf("%lld
    ",ans);
        return 0;
    }

      

      对于石子合并问题我只给出以上三种,孰优孰劣,大家见仁见智。

  • 相关阅读:
    HDFS DataNode 多目录
    HDFS DataNode 退役 旧节点
    HDFS DateNoda 服役 新节点
    HDFS DataNode 时限参数设置
    HDFS NameNode 多目录
    HDFS 安全模式
    HDFS NameNode故障排除
    HDFS CheckPoint时间设置
    HDFS NameNode和SecondaryNameNode
    微信小程序:上拉加载更多
  • 原文地址:https://www.cnblogs.com/NINGLONG/p/7624787.html
Copyright © 2011-2022 走看看