动态规划之切割钢条

1.问题描述

一公司购买长钢条,将其切为短钢条出售,假设切割没有成本,公司希望知道最佳的切割方案!假设我们知道一段长度为i的钢条的价格为pi(i = 1,2,3...),钢条长度均为整英寸,下面给出一个价格表:

长度i 10
价格pi 10 17 17 20 24 30

给定一长度为n的钢条和一张价格表(i =1, 2,3...n),求切割钢条的方案,使的利润最大,可以不进行切割

2.动态规划

动态规划与分治法比较类似,都是通过求解子问题的解,最终来求出原问题的解。分治法只要是将原问题分解成为若干个独立的子问题,通过子问题来求解原问题的解。而动态规划恰恰相反,动态规划是用来求解子问题重叠的情况,即不同的子问题之间具有相同的公共子问题,在这种情况下分治法会花费大量的时间来计算已经计算过的那部分重叠子问题,效率大大降低,而动态规划权衡时间和空间的因素,将已经计算过的子问题存入一张表内,下次遇到相同的子问题时,直接查表就可以得到结果,大大加快了速度

动态规划通常用来求解最优化问题,一般来说一个问题的最优解有多个,动态规划可以求解出其中的一个最优解,而不是最优解

我们通常按照下面的四个步骤来设计一个动态规划问题:

1.刻画一个最优解的结构特征。

2.递归的定义最优解的值

3.计算最优解时,一般采用自下而上的方法来求解

4.利用计算出的信息构造出一个最优解

下面我们来研究怎么用动态规划计算出一个最优解

3.问题分析

长度为n的钢条一共有2^(n-1)种切割方法,因为每一个长度为1的节点处都可以选择切或者不切

那么我们假设长度为n的钢条切成了k条钢条的时候,利润最大:

n = i1+i2+i3+...+ik;

ri = pi1+pi2+pi3+...+pik;

那么最大利润为:

rn = max(pn , r1+rn-1 , r2+rn-2, ...,ri+rn-i , rn-1+r1);(pn为不进行切割的时候的利润,ri+rn-i为在节点i处进行切割,ri为长度为i的钢条最大利润, rn-i为长度n-i的钢条的最大利润)!

这里我么通过组合两个相关子问题的最优解,并在所有可能的切割点上选取出最大的的利润,构成原问题的最优解。我们称钢条切割问题满足最优子结构的性质:问题的最优解由相关子问题的最优解组成,子问题可以独立求解!

我们不妨将上面的问题简化,得到一个简化以后的最优解的结构:

rn = max(p1+rn-1 , p2+rn-2 ,...,pn+r0);

4.代码实现

自顶向下的代码实现:

#include <iostream>
#include <string>
#include <limits.h>

using namespace std;

#define KIND_SIZE 11
/** 每种长度的基本价格 */
int price[]={0,1,5,8,9,10,17,17,20,24,30};

int dealMaxProfit(int n , int maxProfit[]);
/**
 * 得到最大的利润
 * @param  n 钢条长度
 * @return   最大利润
 */
int getMaxProfit(int n)
{
    if( n < 0 || n > KIND_SIZE)
        return 0;
    int maxProfit[KIND_SIZE];//记录每个长度下的最大利润是多少
    for (int i = 0; i <  KIND_SIZE ; ++i)
    {
        maxProfit[i] = INT_MIN;
    }
    maxProfit[0] = 0;
    dealMaxProfit(n , maxProfit);
    return maxProfit[n];
}

/**
 * 将每个长度对应的比n小的钢条最大利润都保存在maxProfit里面
 * @param n         钢条长度
 * @param maxProfit 保存最大利润的数组
 * @return          返回长度为n的最大利润
 */
int dealMaxProfit(int n , int maxProfit[])
{
    if( n == 0)
        return 0;
    /** 表示之前已经算过了 */
    if(maxProfit[n]!= INT_MIN)
        return maxProfit[n];
    /** 没有算过那么就算一遍 */
    int max = INT_MIN;
    for (int i = 1; i < n+1; ++i)
    {
        int temp = price[i]+dealMaxProfit(n-i, maxProfit);
        if(max < temp)
            max = temp;
    }
    maxProfit[n] = max;
    return max;
}

int main(int argc, char const *argv[])
{
    while(1)
    {
        int steelBarLen;
        cout<<"Enter the steel Bar Length(0-10)>";
        cin >> steelBarLen;
        cout<<"Max profit is : "<<getMaxProfit(steelBarLen)<<endl;
    }
    return 0;
}

自底向上的代码实现:

#include <iostream>
#include <limits.h>
#include <string>

using namespace std;
#define KIND_SIZE 11
/** 这个是每一个长度下的单价 */
int price[]={0,1,5,8,9,10,17,17,20, 24 ,30};

int dealMaxProfit(int n , int maxProfit[]);
/**
 * 得到最大的利润
 * @param  n 钢条的长度
 * @return   最大的利润
 */
int getMaxProfit(int n)
{
    if(n < 0 || n > KIND_SIZE)
        return 0;
    int maxProfit[KIND_SIZE];
    for (int i = 0; i < KIND_SIZE; ++i)
    {
        maxProfit[i] = INT_MIN;
    }
    maxProfit[0] = 0;
    dealMaxProfit(n , maxProfit);
    return maxProfit[n];
}

/**
 * 处理得到<n长度的利润
 * @param  n         钢条的长度
 * @param  maxProfit 最大的利润
 * @return           对应长度为n的最大的利润
 */
int dealMaxProfit(int n , int maxProfit[])
{
    if(n == 0)
        return 0;
    for (int i = 1; i <= n; ++i)
    {
        int max = INT_MIN;
        for (int j = 1; j <= i; ++j)
        {// 每次都是将最优子结构求出来,再求上层的
            int temp = price[j]+maxProfit[i-j];
            if(max < temp)
                max = temp;
        }
        maxProfit[i] = max;
    }
}

int main(int argc, char const *argv[])
{
    while(1)
    {
        int steelBarLen;
        cout<<"Enter the steel Bar Length(0-10)>";
        cin >> steelBarLen;
        cout<<"Max profit is : "<<getMaxProfit(steelBarLen)<<endl;
    }
    return 0;
}

但是上面的代码只是求解了最终的最大的利润,并没有算出具体的解决方案

我们不妨观察一下最优解的结构:

rn = max(p1+rn-1 , p2+rn-2 ,...,pn+r0);

最优的解是由子问题的最优解构成,钢条要么在长度为i处切割,要么不切割,所有我们就用一个数组来保存对应长度n应该从位置i(不截断情况i=n)截断的数据

即devidePos[n]=i;

于是我们将上面的算法进行改进,使其可以求出截断的方案

自顶向下的代码实现:

/**
 * 这个是自顶向下的求法
 */
#include <iostream>
#include <string>
#include <limits.h>
 #include <stdio.h>

using namespace std;

#define KIND_SIZE 11
/** 每种长度的基本价格 */
int price[]={0,1,5,8,9,10,17,17,20,24,30};

int dealMaxProfit(int n , int maxProfit[] , int devidePos[]);
/**
 * 得到最大的利润
 * @param  n 钢条长度
 * @return   最大利润
 */
int getMaxProfit(int n , int devidePos[])
{   
    if( n < 0 || n > KIND_SIZE)
        return 0;
    int maxProfit[KIND_SIZE];//记录每个长度下的最大利润是多少
    for (int i = 0; i <  KIND_SIZE ; ++i)
    {
        maxProfit[i] = INT_MIN;
        devidePos[i] = i; 
    }
    maxProfit[0] = 0;
    dealMaxProfit(n , maxProfit , devidePos);
    return maxProfit[n];
}

/**
 * 将每个长度对应的比n小的钢条最大利润都保存在maxProfit里面
 * @param n         钢条长度
 * @param maxProfit 保存最大利润的数组
 * @return          返回长度为n的最大利润
 */
int dealMaxProfit(int n , int maxProfit[] , int devidePos[])
{
    if( n == 0)
        return 0;
    /** 表示之前已经算过了 */
    if(maxProfit[n]!= INT_MIN) 
        return maxProfit[n];
    /** 没有算过那么就算一遍 */
    int max = INT_MIN;
    int pos = n;xia
    for (int i = 1; i < n+1; ++i)
    {
        int temp = price[i]+dealMaxProfit(n-i, maxProfit  ,devidePos); 
        if(max < temp)
        {
            max = temp;
            pos = i;
        }
    }
    maxProfit[n] = max;
    devidePos[n] = pos;
    return max;
}

void printCutSolution(int n , int devidePos[])
{
    if(n < 0 || n >= KIND_SIZE)
        return ;
    if( n == devidePos[n])
    {
        printf("%s\n", "not devide" );
        return;
    }
    printf("%d steel bar devide into %d and %d \n", n , n - devidePos[n] , devidePos[n] );
    printCutSolution(n - devidePos[n] , devidePos);
}

int main(int argc, char const *argv[])
{

    while(1)
    {   
        int devidePos[KIND_SIZE];
        int steelBarLen; 
        cout<<"Enter the steel Bar Length(0-10)>";
        cin >> steelBarLen;
        cout<<"Max profit is : "<<getMaxProfit(steelBarLen , devidePos)<<endl;
        printCutSolution(steelBarLen , devidePos);
    }
    return 0;

}

自底向上的代码实现:

#include <iostream>
#include <limits.h>
#include <stdio.h>
#include <string>

using namespace std;
#define KIND_SIZE 11
/** 这个是每一个长度下的单价 */
int price[]={0,1,5,8,9,10,17,17,20, 24 ,30};

int dealMaxProfit(int n , int maxProfit[] , int devidePos[]);
/**
 * 得到最大的利润
 * @param  n 钢条的长度
 * @return   最大的利润
 */
int getMaxProfit(int n , int devidePos[])
{
    if(n < 0 || n > KIND_SIZE)
        return 0;
    int maxProfit[KIND_SIZE];
    for (int i = 0; i < KIND_SIZE; ++i)
    {
        maxProfit[i] = INT_MIN;
        devidePos[i] = i;
    }
    maxProfit[0] = 0;
    dealMaxProfit(n , maxProfit , devidePos);
    return maxProfit[n];
}

/**
 * 处理得到<n长度的利润
 * @param  n         钢条的长度
 * @param  maxProfit 最大的利润
 * @return           对应长度为n的最大的利润
 */
int dealMaxProfit(int n , int maxProfit[] , int devidePos[])
{
    if(n == 0)
        return 0;
    for (int i = 1; i <= n; ++i)
    {
        int max = INT_MIN;
        int pos=i;
        for (int j = 1; j <= i; ++j)
        {// 每次都是将最优子结构求出来,再求上层的
            int temp = price[j]+maxProfit[i-j];
            if(max < temp)
            {
                max = temp;
                pos = j;
            }
        }
        devidePos[i] = pos;
        maxProfit[i] = max;
    }
}

void printCutSolution(int n , int devidePos[])
{
    if(n < 0 || n >= KIND_SIZE)
        return ;
    if( n == devidePos[n])
    {
        printf("%s\n", "not devide" );
        return;
    }
    printf("%d steel bar devide into %d and %d \n", n , n - devidePos[n] , devidePos[n] );
    printCutSolution(n - devidePos[n] , devidePos);
}

int main(int argc, char const *argv[])
{
    while(1)
    {
        int devidePos[KIND_SIZE];
        int steelBarLen;
        cout<<"Enter the steel Bar Length(0-10)>";
        cin >> steelBarLen;
        cout<<"Max profit is : "<<getMaxProfit(steelBarLen , devidePos)<<endl;
        printCutSolution(steelBarLen , devidePos);
    }
    return 0;
}

之前我么在描述问题的时候假设过切割钢条不消耗费用,假设现在改变条件,钢条每切割一次,代价为c,那么最佳的决绝方案是什么呢?其实这个问题和上面的问题比较类似,我们只用每次减去代价c就可以了,代码实现如下:

#include <iostream>
#include <limits.h>
using namespace std;

#define KIND_SIZE 11
#define COST 2 /** 切割的花费 */
/** 每种长度的基本价格 */
int price[]={0,1,5,8,9,10,17,17,20,24,30};

int dealMaxProfit(int , int *);
int getMaxProfit(int n)
{
    if(n <=0 || n >= KIND_SIZE)
        return 0;
    int maxProfit[KIND_SIZE];
    for (int i = 0; i <KIND_SIZE; ++i)
    {
        maxProfit[i] = INT_MIN;
    }
    dealMaxProfit(n , maxProfit);
    return maxProfit[n];
}

int dealMaxProfit(int n , int maxProfit[])
{
    maxProfit[0] = 0;
    for (int i = 1; i < KIND_SIZE; ++i)
    {
        int max = INT_MIN;
        for (int j = 1; j < i ; ++j)
        {
            int maxTemp = price[j]+maxProfit[i-j]-COST;
            if(max < maxTemp)
                max = maxTemp;
        }
        max = max>price[i]?max:price[i];
        maxProfit[i] = max;
    }
    return maxProfit[n];
}

int main(int argc, char const *argv[])
{
    while(1)
    {
        int steelBarLen;
        cout<<"Enter the steel Bar Length(0-10)>";
        cin >> steelBarLen;
        cout<<"Max profit is : "<<getMaxProfit(steelBarLen)<<endl;
    }
    return 0;
}

时间: 2024-10-15 00:21:03

动态规划之切割钢条的相关文章

切割钢条【动态规划】

假设公司出售一段长度为i英寸的钢条的价格为Pi(i = 1, 2, ...单位:美元),下面给出了 价格表样例: 长度i    1     2     3     4     5     6     7    8     9    10 价格Pi  1     5     8     9    10   17   17  20   24    30 切割钢条的问题是这样的:给定一段长度为n英寸的钢条和一个价格表Pi,求切割方案,使 得销售收益Rn最大.当然,如果长度为n英寸的钢条价格Pn足够大,

动态规划--链条切割问题

钢条切割问题:给定一段长度为n英寸的钢条和一个价格表pi(i=1,2,...,n)求切割钢条方案,使得销售收益rn最大.  注意,如果长度为n英寸的钢条的价格pn足够大,最优解可能就是完全不需要切割. 思路:先将钢条切成两条,有n-1种方案,每一种方案的最优解都等于两个子钢条的最优解.我们从这n-1个伪最优解再挑出最优的解了 以下是伪代码: 1 CUT-ROD(p,n) 2 if n == 0 3     return 0 4 q=负无穷 5 for i = 1 to n 6     q=max

算法导论动态规划 15.1-3 钢条切割问题

算法导论的第一个动态规划问题--钢条切割 我们知道钢条的长度和价格为: 长度i 1 2 3 4 5 6 7 8 9 10 价格pi 1 5 8 9 10 17 17 20 24 30 书上的两种方法已经很清楚,这里主要说一下课后练习里面15-3钢条成本加上切割成本,即要切割的次数最少.15-4返回切割方案 #include<fstream> #include<iostream> using namespace std; int main() { int const N = 11;

NOI题库 / 2.6基本算法之动态规划 - 8471:切割回文

总时间限制:  1000ms 内存限制:  65536kB 描述 阿福最近对回文串产生了非常浓厚的兴趣. 如果一个字符串从左往右看和从右往左看完全相同的话,那么就认为这个串是一个回文串.例如,"abcaacba"是一个回文串,"abcaaba"则不是一个回文串. 阿福现在强迫症发作,看到什么字符串都想要把它变成回文的.阿福可以通过切割字符串,使得切割完之后得到的子串都是回文的. 现在阿福想知道他最少切割多少次就可以达到目的.例如,对于字符串"abaacca

算法导论-动态规划-钢条切割

动态规划通常用于解决最优化问题,在这类问题中,通过做出一组选择来达到最优解.在做出每个选择的同时,通常会生成与原问题形式相同的子问题.当多于一个选择子集都生成相同的子问题时,动态规划技术通常就会很有效,其关键技术就是对每个这样的子问题都保存其解,当其重复出现时即可避免重复求解. 钢条切割问题 Serling公司购买长钢条,将其切割为短钢条出售.切割工序本身没有成本支出.公司管理层希望知道最佳的切割方案.假定我们知道Serling公司出售一段长为i英寸的钢条的价格为pi(i=1,2,…,单位为美元

动态规划 钢条切割问题

#include <stdio.h> /* *钢条切割问题: *问题描述 假设公司出售一段长度为i英寸的钢条的价格为Pi(i = 1, 2, ...单位:美元),下面给出了价格表样例: 长度i 1 2 3 4 5 6 7 8 9 10 价格Pi 1 5 8 9 10 17 17 20 24 30 切割钢条的问题是这样的:给定一段长度为n英寸的钢条和一个价格表Pi,求切割方案,使得销售收益Rn最大. */ //假定价格一开始已经给出了,用全局的写死 #define max(x,y) (x)>

算法导论---------动态规划之钢条切割

动态规划方法通常用来求解最优化问题.动态规划算法设计步骤: 1.刻画一个最优解的结构特征. 2.递归定义最优解的值. 3.计算最优解的值,通常采用自底向上的方法. 4.利用计算出的信息构造一个最优解. 动态规划的实现方法: 带备忘的自顶向下法:此方法仍按自然的递归形式编写过程,但过程会保存每个子问题的解(通常保存在一个数组或散列表中).当需要一个子问题的解时,过程首先检查是否已经保存过此解.如果是,则直接返回保存的值,从而节省了计算时间:否则,按通常方式计算这个子问题. 自底向上法:这种方法一般

【算法设计-动态规划】钢条切割问题

问题:给定一段长度为n英寸的钢条和一个价格表pi(i=1,2,...,n),求切割钢条方案,使得销售收益rn最大.如果长度为n英寸的钢条的价格pn足够大,最优解可能就是完全不需要切割. 方法一:递归 从上而下把所有的全部搜索一遍 int CUT_ROD(int p[],int n) { if(n==0) return 0; int q=INT_MIN; for(int i=1;i<=n;i++) { q=max(q,p[i]+CUT_ROD(p,n-i)); printf("n=%d&qu

第十五章 动态规划——钢条切割

前言:动态规划的概念 动态规划(dynamic programming)是通过组合子问题的解而解决整个问题的.分治算法是指将问题划分为一些独立的子问题,递归的求解各个问题,然后合并子问题的解而得到原问题的解.例如归并排序,快速排序都是采用分治算法思想.本书在第二章介绍归并排序时,详细介绍了分治算法的操作步骤,详细的内容请参考:http://www.cnblogs.com/Anker/archive/2013/01/22/2871042.html.而动态规划与此不同,适用于子问题不是独立的情况,也