zoukankan      html  css  js  c++  java
  • 合并石子(区间DP)

    有N堆石子,现要将石子有序的合并成一堆,规定如下:每次只能移动相邻的2堆石子合并,合并花费为新合成的一堆石子的数量。求将这N堆石子合并成一堆的总花费最小。

    区间DP思想:现在小区间进行DP得到最优解,然后再利用小区间的最优解组合并求大区间的最优解。(需要从小到大枚举所有可能的区间)
    代码(没提交过,不过应该正确):

    include

    using namespace std;
    const int maxn1=300;
    int main()
    {
    int n,a[maxn1]={0},sum[maxn1]={0};
    cin>>n;
    for(int i=1;i<=n;i++)
    cin>>a[i],sum[i]=a[i]+sum[i-1]; //前缀和
    int dp[maxn1][maxn1]={0}; //dp[i][j]表示区间[i,n]的最小花费
    for(int len=1;len<n;len++) //枚举所有可能的区间
    {
    for(int i=1;i<=n-len;i++)
    {
    dp[i][i+len]=INT_MAX;
    for(int k=i;k<i+len;k++)
    dp[i][i+len]=min(dp[i][i+len],dp[i][k]+dp[k+1][i+len]+sum[i+len]-sum[i-1]);
    }
    }
    cout<<dp[1][n]<<endl;
    return 0;
    }
    这样写很容易理解,但复杂度为O(n^3),基本最多只能计算250堆石子。
    在网上看了一个平行四边形对区间DP的优化,复杂度基本可以维持在O(n2),但是我却始终不理解其原理,不过大概按葫芦画瓢,写了一个(并注释了与之前O(n3)的不同):

    include

    using namespace std;
    const int maxn1=3000;
    int main()
    {
    int n,a[maxn1]={0},sum[maxn1]={0},s[maxn1][maxn1];
    cin>>n;
    for(int i=1;i<=n;i++)
    cin>>a[i],sum[i]=a[i]+sum[i-1];
    for(int i=1;i<=n;i++) //多加
    s[i][i]=i; //多加 (记录区间最优解的位置)
    int dp[maxn1][maxn1]={0};
    for(int len=1;len<n;len++)
    {
    for(int i=1;i<=n-len;i++)
    {
    dp[i][i+len]=INT_MAX;
    for(int k=s[i-1][i+len];k<=s[i+1][i+len];k++) //改变
    if(dp[i][k]+dp[k+1][i+len]+sum[i+len]-sum[i-1]<dp[i][i+len]) //改变
    {
    dp[i][i+len]=dp[i][k]+dp[k+1][i+len]+sum[i+len]-sum[i-1]; //改变
    s[i][i+len]=k; //改变
    }
    }
    }
    cout<<dp[1][n]<<endl;
    return 0;
    }


    作者:孙建钊
    出处:http://www.cnblogs.com/sunjianzhao/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 相关阅读:
    c++虚函数
    Boost Graph Library
    二分查找 http://ac.jobdu.com/problem.php?pid=1545
    二叉树应用
    注解篇二
    springboot+fastdfs+docker
    注解篇一
    vue启动加修改例子
    SSM框架中的中文乱码问题
    mybatis4
  • 原文地址:https://www.cnblogs.com/sunjianzhao/p/11385092.html
Copyright © 2011-2022 走看看