01揹包问题的空间优化

oj地址:01揹包
首先说一下这道题引发的思考,我习惯把状态转移方程称作小单元,它一般是以一个单元格为计算单位的递推过程,然后在小单元之上,我又把行与行之间的关系,称作大单元。很多动态规划的题,一个单元格一般依赖于其他几个单元格,这是从单元格的角度上去看问题,如果从行的角度去看问题,很多的都是一行上的数据,只会依赖固定的一行或者几行,这里就是空间优化的出发点。比如一个二维表的空间是O(mn)的,而发现其第i行永远只依赖第i-1行,那么就可以只给它分配两行,然后循环使用。
1.最初始的解决方法。

import java.util.*;
public class Main{
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        while(sc.hasNext()){
            int C=sc.nextInt();
            int N=sc.nextInt();
            int[] V=new int[N+1];//评价
            int[] P=new int[N+1];//价格
            int[][] dp=new int[N+1][C+1];//表示dp[i][j]表示前i件放入容量为j的揹包时的最大评价
 
            for(int i=1;i<=N;i++){
                P[i]=sc.nextInt();
                V[i]=sc.nextInt();
            }
 
            //第一行都为0,java里省去初始化
           for(int i=1;i<=N;i++){
               for(int j=0;j<=C;j++){
                   if(j-P[i]>=0){
                       dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-P[i]]+V[i]);
                   }else{
                       dp[i][j]=dp[i-1][j];
                   }
               }
           }
            System.out.println(dp[N][C]);
        
        }
         
    }
}

2.在1的基础上,发现在动态规划的过程中,永远是一行和上一行之间的依赖关系。那么给它分配两行,循环使用就行了。

import java.util.*;
public class Main{
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        while(sc.hasNext()){
            int C=sc.nextInt();
            int N=sc.nextInt();
            int[] V=new int[N+1];//评价
            int[] P=new int[N+1];//价格
            int[][] dp=new int[2][C+1];//这次只分配两行给它
 
            for(int i=1;i<=N;i++){
                P[i]=sc.nextInt();
                V[i]=sc.nextInt();
            }
 
            //第一行都为0,java里省去初始化
           for(int i=1;i<=N;i++){
               for(int j=0;j<=C;j++){
               		//来回倒腾,不要浪费
                   dp[i%2][j] = dp[(i-1)%2][j];
                   if(j-P[i]>=0){
                       dp[i%2][j]=Math.max(dp[(i-1)%2][j],dp[(i-1)%2][j-P[i]]+V[i]);
                   }else{
                       dp[i%2][j]=dp[(i-1)%2][j];
                   }
               }
           }
            System.out.println(dp[N%2][C]);
        
        }
         
    }
}

3.在2的基础上,对于一个小单元来说,它永远只需要自己上方,和自己左方的元素,右边的不需要。如果把两行的空间优化成一行。计算当前行,从右往左修改,修改过的值,就是已经固定了的当前行的所求值,还待修改的,就是上一行的遗留值(供后面的使用),当前行和上一行在一行里同时体现,而且互相不影响,修改的过程中,上一行逐渐变成这一行。

import java.util.*;
public class Main{
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        while(sc.hasNext()){
            int C=sc.nextInt();
            int N=sc.nextInt();
            int[] V=new int[N+1];//评价
            int[] P=new int[N+1];//价格
            int[] dp=new int[C+1];//一行的话,直接一维数组
 
            for(int i=1;i<=N;i++){
                P[i]=sc.nextInt();
                V[i]=sc.nextInt();
            }
 
            //第一行都为0,java里省去初始化
            
           for(int i=1;i<=N;i++){
               for(int j=C;j>=0;j--){
                   if(j-P[i]>=0){
                       //注意这里的细节,左边的dp[j],表示正在修改的当前行,右边的dp[j]表示上一行,虽然都是dp[j],但是意义不同
                       dp[j]=Math.max(dp[j],dp[j-P[i]]+V[i]);
                   }
                   /**
                   看不懂的话,加上这句就看懂了
                   else{
                   	 dp[j]=dp[j];
                   }
                   */
               }
           }
            System.out.println(dp[C]);
        
        }
         
    }
}

4.常数级优化1:单纯的coding优化。二层for循环的逻辑,这样写是一样的。

import java.util.*;
public class Main{
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        while(sc.hasNext()){
            int C=sc.nextInt();
            int N=sc.nextInt();
            int[] V=new int[N+1];//评价
            int[] P=new int[N+1];//价格
            int[] dp=new int[C+1];//表示dp[i][j]表示前i件放入容量为j的揹包时的最大评价
 
            for(int i=1;i<=N;i++){
                P[i]=sc.nextInt();
                V[i]=sc.nextInt();
            }

            //第一行都为0,java里省去初始化
            
           for(int i=1;i<=N;i++){
               for(int j=C;j>=P[i];j--){
                       dp[j]=Math.max(dp[j],dp[j-P[i]]+V[i]);
               }
           }
            System.out.println(dp[C]);
        }
         
    }
}

5.常数级优化2:揹包九讲里的常数级优化,优化下限公式如图,但是我感觉会增加计算或者增加空间。没去实现。
常数优化

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章