背包问题之回溯和动态规划

2018/10/9美团笔试编程第一题(Android)

题意大概是:输入n表示有n份菜,输入X表示优惠券满X减10,接下来n个数表示每份菜的价格,每份菜最多点一份。求使用优惠券的最少金额。

这是一道很有意思的题目,其实0-1背包问题的变体,关于0-1背包问题,我也只懂回溯法和动态规划。在此再复习一下。

背包问题:背包最大可装物品重量总共为w,每件物品为w[i],价值为v[i],求容量内可装最大价值。

动态规划

dp[i][j] 表示在考虑到了第i件商品,背包最大容量为j时,可获得的最大价值。
状态传递方程为 dp[i][j] = dp[i-1][j] ,此时是 w[i] > j,表明背包装不下该物品。当装得下时,则要考虑装入与不装入哪一种更具有价值,传递方程为 dp[i][j] = max(dp[i-1][j],dp[i-1][j-w[i]]+v[i])。以上

回溯法

回溯法采用递归搜索dfs,当搜到满足要求的点时更新当前的value,然后剪枝返回,不需要往下搜(甚至可以利用剪枝函数再次优化)。当搜到底时,说明搜不到要求的解,失败了返回。

题目变形

上面的编程题可以看成在菜品总价减去优惠券得到差价,求不超过差价的前提下尽可能多的装入菜,也就是最终要排除的菜的总价最高是多少,剩下的就是题目要求的最少。编程思想是相同的:


import java.util.Arrays;
import java.util.Scanner;

public class Main {
	//回溯法
	int min = Integer.MAX_VALUE;

    public static void main(String[] args) {
    	Scanner sc = new Scanner(System.in);
    	int n = sc.nextInt();
    	int X = sc.nextInt();
    	int[] array = new int[n];
    	int sum = 0;
    	for(int i=0;i=0;j--) {
        		if(array[i]>j) break;
        		dp[j] = Math.max(dp[j-array[i]] + array[i], dp[j]);
        	}
        }
        return sum - dp[m];
    }
    */
    
    
    /**
     * 0-1背包问题变体 动态规划
     * @param n
     * @param X
     * @param array
     * @param sum
     * @return
     */
    public int getResult(int n,int X,int[] array,int sum) {
    	//都不满足
    	if(sum < X) return -1;
    	//差额,即背包“容量”
    	int m = sum -X;
    	int[][]dp = new int[n+1][m+1];
    	//初始状态,i==0表示没有菜品,j==0表示”容量“为0,价值自然也为0
    	for(int i=0;in) return;	
    	//0不放入,1放入
    	for(int i=0;i<2;i++) {
    		if(i==0) {
    			findAns(p+1,n,X,array,total);
    		}else {
    	    	if(total + array[p] < X) {
    	    		//继续寻找
    	    		findAns(p+1,n,X,array,total+array[p]);
    	    	}else {
    	    		//找到了剪枝了,不再寻找
    	    		int current = total+array[p];
    	    		min = min < current ? min :current;
    	    	}
    		}
    	}
    }
}

你可能感兴趣的:(算法与数据结构)