钢铁切割(动态规划详解)

题目概要:
给定一段钢条,和不同长度的价格,问如何切割使得总价格最大。
ex :
例子数据
为了求解规模为 n 的原问题,我们先求解形式完全一样,但规模更小的子问题。 即当完成首次切割后,我们将两段钢条看成两个独立的钢条切割问题实例。 我们通过组合相关子问题的最优解,并在所有可能的两段切割方案中选取组合收益最大者,构成原问题的最优解。

最优子结构:

问题的最优解由相关子问题的最优解组合而成,而这些子问题可以独立求解。

动态规划的两种实现方法:

带备忘的自顶向下法(记忆化搜索)。
自底向上法(将子问题按规模排序,类似于递推)。

算法导论用子问题图上按照逆拓扑序求解问题,引出记忆化搜索。
重构解(输出方案):转移的时候记录最优子结构的位置。

除了动态规划的方法还有递归求解的方法。

附上代码:
注意看注释

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N=1e3+10;
int n;
int w[N];
int r[N];       //记录切割子值的最大价值
int dp[N];
int test1(int n)// 自顶向下递归
{
    
    
    int res=-1;
    if(n==0)return 0;
    for(int i=1; i<=n; i++)
    {
    
    
        res=max(res,w[i]+test1(n-i));
    }
    return res;
}

int test2(int n)// 自顶向下 记忆化 递归搜索
{
    
    
    memset(r,-1,sizeof r);
    int res=-1;
    if(r[n]!=-1)return r[n];  //之前算过的直接return,记忆的优化
    if(n==0)return r[n]=0;   //记得特殊情况判出
    for(int i=1; i<=n; i++)
    {
    
    
        res=max(res,w[i]+test2(n-i));
    }
    return r[n]=res; //直接在return的时候记忆。
}

int test3(int n)   //自底向上的动态规划
{
    
    
    memset(r,0,sizeof r);
    for(int i=1; i<=n; i++)
    {
    
    
        int res=0;
        for(int j=1; j<=i; j++)
        {
    
    
            res=max(res,w[j]+r[i-j]);
        }
        r[i]=res;
    }
    return r[n];
}

int main()
{
    
    
    cin>>n;
    for(int i=1; i<=n; i++)cin>>w[i]; //不同长度的价值;
    //三种方法
    cout<<test1(n)<<endl;
    cout<<test2(n)<<endl;
    cout<<test3(n)<<endl;

    return 0;
}
/*
10
1 5 8 9 10 17 17 20 24 30
*/

1.自顶向下递归

递归的意义就是递归成子问题,子问题再分解成子问题的子问题,再利用递归回的值返回到原问题,但是递归的方式是化成小问题,但是小问题的数量和不记忆使得递归的时间较长,效率过低,而且是从上到下递归,没有记忆化,但是在数据范围较少时,可以使用,代码比较好想一些。

2.自顶向下 记忆化 递归搜索

故名思意,带了一个记忆化,但是递归的话还是比较慢,这算是对递归的一个优化。

3.自底向上动态规划

通过小状态来递推出大状态,并且记忆,使得整个过程较快,并且做到了有记忆。

猜你喜欢

转载自blog.csdn.net/weixin_51626694/article/details/121524689