动态规划怎么理解

网上资料一大堆,没找到一个能说清楚的。结合不清不楚的描述,我研究一番,来尝试解释一下。

那么这个动态规划的基本思路是什么?
首先是数据的初始化,然后是状态转移方程。
举个例子
斐波那契数列:1,1,2,3,5,8,13
代码:

int[] dp = new int[n]; //存放所有数据 dp[n] 就是 第n个数的值
dp[1] = 1; //初始化数据
dp[2] = 1; //初始化数据
for(int i = 2; i < n; i++){ //直接在第三个开始计算
    dp[i] = dp[i-1] + arr[i-2]; //转移方程
}


然后是01背包问题:
描述:
有N件物品和一个容量为V的背包。第i件物品的体积是v[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。
01背包的特点就是:每种物品仅有一件,可以选择放或不放。
如何初始化?
这个一维数组就放不下了,有两个维度,几件物品,多少容量?

int[][] dp = new int[N+1][V+1]; 为什么是N+1,因为代码要处理物品为0的情况。
dp[0][V] = 0; //意义 一件物品也没有,不管有多少容量 价值为0 代码可以省略,int默认就是0
dp[N][0] = 0; //意义 没有容量,有多少物品也放不下 价值为0 代码可以省略,int默认就是0
状态转移
for(int i = 1; i <= N; i++){ //物品数量递增 直接从1开始 因为 dp[0][V] = 0;
    for(int j = 1; j <= V; j++){ //容量递增 直接从1开始 因为 dp[N][0] = 0;
        if(j > wi){ //容量够
            dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-vi]+wi); // dp[i-1][j] 这个的意义是不放这件物品的价值。
            // dp[i-1][j-vi]+wi 这个的意义是放这件物品的价值。既然要放肯定要把价值wi加上,
            //而dp[i-1][j-vi],取出该物品占用的空间,放置i-1件物品的最大价值,这个东西在前面已经算过了,直接用。
            //比如我现在是第一个物品体积5,价值6,背包容量有5, 能放,然后放的话价值是6,不放的话价值是0,得放。
            //代码就是 i = 1;v1 = 5;w1 = 6; dp[1][5] = dp[1-1][5] + w1;
            //然后又来一个物品体积6,价值5,背包容量增加到10,10 > 6 也能放,要放的话,得把第一个物品拿掉,价值变成了5,反而小了,怎么着? 不放呗。
            //代码就是 i = 2; v2 =6; w2 = 5; dp[2][10] = dp[2-1][10-6] + w2; 其中 dp[1][10-6] = 0;
            //然后还是这个物品,但是我的背包容量增加到了11, 11 > 6 能放,而且第一个物品还不要拿掉 价值变成了11,得放。
            //代码就是 i = 2; v2 = 6; w2 = 5; dp[2][11] = dp[2-1][11-6] + w2; 其中dp[2-1][11-6] 上面已经算过了是6 。
        }
    }
}

还有挺多变化的,遇到再整理吧。代码没有编译,不保证运行,欢迎批评指正。

写了一个复杂一些的,不贴一下,显得没有说服力。牛客网购物单问题,亲测可用:

 /**
     * 18 购物单
     * 1000 5
     * 800 2 0
     * 400 5 1
     * 300 5 1
     * 400 3 0
     * 500 2 0
     * @param args
     */
    public static void main(String[] args) {
        Scanner ac = new Scanner(System.in);
        int N = ac.nextInt() / 10;
        if(N >= 3200){
            return;
        }
        int m = ac.nextInt();
        if(m >= 60){
            return;
        }
        Goods[] goodsArr = new Goods[m+1];
        goodsArr[0] = new Goods(0,0,0); //控制初始化
        for(int i = 1; i <= m; i++){
            int v = ac.nextInt() / 10; //价格
            int p = ac.nextInt(); //重要度
            int q = ac.nextInt(); //标识
            Goods goods = new Goods(v,v*p,q);
            goodsArr[i] = goods;
        }
        for(Goods goods : goodsArr){
            if(goods.q != 0){
                //附件
                if(goodsArr[goods.q].fGoodsArr[0] == null){
                    goodsArr[goods.q].fGoodsArr[0] = goods;
                }else{
                    goodsArr[goods.q].fGoodsArr[1] = goods;
                }
            }
        }

        int[][] dp = new int[m+1][N+1];
        for(int i = 1;i <= m;i++){ //从1 开始 默认 int[0][j] = 0,意义就是一个物品也不放 满意度就是0
            for(int j = 1; j <= N; j++){ //同上,默认int[i][0] = 0,意义就是一分钱也没有,满意度就是0
                Goods curGoods = goodsArr[i];
                if(curGoods.q == 0){ //钱够买第i个物品 并且是主件
                    if(j >= curGoods.price){
                        //如要买,这是满意度,其中 dp[i-1][j-curGoods.price] 这个的意义是总钱数减去当前物品的钱数,能够购买i-1个物品的最大满意度
                        //比如我有1000 快需要再买一个400块的东西,满意度应该是 600块能够购买的最大满意度减伤当前物品的满意度, 动态规划 600的已经算过了 直接拿来用
                        int myd = dp[i-1][j-curGoods.price]+curGoods.myd;
                        dp[i][j] = Math.max(dp[i-1][j],myd);
                    }

                    if(curGoods.fGoodsArr[0] != null && i >= 2){
                        //有附件
                        int price = curGoods.price + curGoods.fGoodsArr[0].price;
                        if(j >= price){
                            dp[i][j] = Math.max(dp[i-1][j],dp[i-2][j-price]+ curGoods.myd+ curGoods.fGoodsArr[0].myd);
                        }
                        if(curGoods.fGoodsArr[1] != null){
                            price = curGoods.price + curGoods.fGoodsArr[1].price;
                            if(j >= price){
                                dp[i][j] = Math.max(dp[i-1][j],dp[i-2][j-price]+ curGoods.myd+ curGoods.fGoodsArr[1].myd);
                            }
                            if(i >=3){
                                price = curGoods.price + curGoods.fGoodsArr[0].price + curGoods.fGoodsArr[1].price;
                                if(j >= price){
                                    dp[i][j] = Math.max(dp[i-1][j],dp[i-3][j-price]+ curGoods.myd + curGoods.fGoodsArr[0].myd + curGoods.fGoodsArr[1].myd);
                                }
                            }
                        }
                    }
                }
            }
        }
        System.out.println(dp[m][N]*10);
    }

    static class Goods{
        int price;
        int myd;
        int q;
        Goods[] fGoodsArr = new Goods[2];
        public Goods(int price,int myd,int q){
            this.price = price;
            this.myd = myd;
            this.q = q;
        }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

lx18854869896

和谐社会靠你了,老铁...

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

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

打赏作者

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

抵扣说明:

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

余额充值