算法设计: 三、动态规划 (1. 0-1 背包问题)—— java实现 - 算法分析

"本文介绍了动态规划算法思想,特别是针对0-1背包问题的解决方案。动态规划通过将问题分解为子问题并存储子问题的解来避免重复计算,提高效率。0-1背包问题中,每种物品只能取0个或1个,目标是找到最大价值。算法通过状态转移方程`f[i][j]=Max{f[i-1][j-Wi]+Vi, f[i-1][j]}
摘要由CSDN通过智能技术生成

动态规划算法思想

把待求解问题分解成若干个子问题,先求解子问题,然后由这些子问题的解得到原问题的解,但动态规划求解过的子问题的结果会被保留下来,不像递归那样每个子问题的求解都要从头开始返回求解。

动态规划求解问题的关键在于获得各个阶段子问题的递推关系式:
  1. 分析原问题的最优解性质,刻画其结构特征。
  2. 递归定义最优值。
  3. 自底向上(由后向前)的方式计算最优值。
  4. 根据计算最优值时得到的信息,构造一个最优解。

动态规划问题的适用条件:

适用动态规划的问题必须满足最优化原理和无后效性。

一、最优化原理(最优子结构性质)

最优化原理可这样阐述:一个最优化策略具有这样的性质,不论过去状态和决策如何,对前面的决策所形成的状态而言,余下的诸决策必须构成最优策略。简而言之,一个最优化策略的子策略总是最优的。一个问题满足最优化原理又称其具有最优子结构性质。

二、无后向性

将各阶段按照一定的次序排列好之后,对于某个给定的阶段状态,它以前各阶段的状态无法直接影响它未来的决策,而只能通过当前的这个状态。换句话说,每个状态都是过去历史的一个完整总结。这就是无后向性,又称为无后效性。

三、子问题的重叠性

动态规划将原来具有指数级复杂度的搜索算法改进成了具有多项式时间的算法。其中的关键在于解决冗余,这是动态规划算法的根本目的。动态规划实质上是一种以空间换时间的技术,它在实现的过程中,不得不存储产生过程中的各种状态,所以它的空间复杂度要大于其它的算法。

0-1 背包问题:

我们有 n 种物品,物品 j 的重量为 wj,价格为 pj。所有物品的重量和价格都是非负的。背包所能承受的最大重量为 c。限定每种物品只能选择 0 个或 1 个。实现计算得出可取的最大价值。

有六件物品,体积和价值见下表,背包容量为 25。

i(物品)123456
w(体积)117912310
v(价值)10579212

求解的思想:
(1)背包容量不足以放下第 i 件物品,只能选择不拿:m[ i ][ j ] = m[ i-1 ][ j ]
(2)背包容量可以放下第 i 件物品,我们就要考虑拿这件物品是否能获取更大
的价值。
(3)综合以上两种情况可以得到状态转换方程 :
f[i,j]=Max{ f[i-1,j-Wi]+Vi( j >= Wi ),f[i-1,j]

源码:

package backtrack;
/*  * 0-1背包问题演示
     * @param V 背包容量
     * @param N 物品种类
     * @param weight 物品重量
     * @param value 物品价值
     * @return
     */
public class ZeroOneKnapsackBacktrack {

	public static void main(String[] args){
		int V=25;	//背包空间
		int N=6;	//物品个数
		int[] weight={11,7,9,12,3,10};	//物品体积
		int[] value={10,5,7,9,2,12};	//物品价值
		//初始化动态规划数组
        int[][] dp = new int[N+1][V+1];
        //为了便于理解,将dp[i][0]和dp[0][j]均置为0,从1开始计算
        for(int i=1;i<N+1;i++){
            for(int j=1;j<V+1;j++){
                //如果第i件物品的重量大于背包容量j,则不装入背包
                //由于weight和value数组下标都是从0开始,故注意第i个物品的重量为weight[i-1],价值为value[i-1]
                if(weight[i-1] > j)
                    dp[i][j] = dp[i-1][j];
                else
                    dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-weight[i-1]]+value[i-1]);
            }
        }
        //则容量为V的背包能够装入物品的最大值为
        int maxValue = dp[N][V];
        //逆推找出装入背包的所有商品的编号
        int j=V;
        String numStr="";
        for(int i=N;i>0;i--){
            //若果dp[i][j]>dp[i-1][j],这说明第i件物品是放入背包的
            if(dp[i][j]>dp[i-1][j]){
                numStr = i+","+numStr;
                j=j-weight[i-1];
            }
            if(j==0)
                break;
        }
        System.out.println("最大价值:"+maxValue+"\n"+"装入的物品编号是:"+numStr);       
    }
}

算法分析

时间复杂度:O(nc),n为物品数,c为背包容量。当背包容量c很大时,算法需要的计算时间较多。例如,当c>2n时,算法需要Ω(n2n)计算时间。

空间复杂度:O(nc),n为物品数,c为背包容量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Whitemeen太白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值