题意整理

  • 给定x,y,以及若干数对[ai,bi,ci]。
  • 从中选出部分数对,使得ai数对的和不小于x,bi数对的和不小于y,求最小的ci数对的和。

方法一(动态规划)

1.解题思路

  • 状态定义:dp[i][j][k]表示选择包装数为i,面包数为j,饮料数为k的情况下,最小的花费。
  • 状态初始化:不选择任何包装时,花费为0。
  • 状态转移:如果j大于等于breadNum是正常的01背包问题,直接从背包取,如果j小于breadNum,仍然从背包取,只是应该从状态为0的位置转移,对于k的分析也是类似。即图片说明

2.代码实现

import java.util.*;

public class Solution {
    /**
     * 
     * @param breadNum int整型 
     * @param beverageNum int整型 
     * @param packageSum int整型二维数组 每个一维数组中有三个数,依次表示这个包装里面的面包数量、饮料数量、花费
     * @return int整型
     */
    public int minCost (int breadNum, int beverageNum, int[][] packageSum) {
        int n=packageSum.length;
        /*初始化dp数组,第一维表示当前包装数量,第二维表示面包数,
        第三维表示饮料数,整体表示当前最小的花费*/
        int[][][] dp=new int[n+1][breadNum+1][beverageNum+1];
        for(int i=0;i<=n;i++){
            for(int j=0;j<=breadNum;j++){
                Arrays.fill(dp[i][j],Integer.MAX_VALUE/2);
            }
        }
        //不选择任何包装时,花费为0
        dp[0][0][0]=0;

        for(int i=1;i<=n;i++){
            for(int j=0;j<=breadNum;j++){
                for(int k=0;k<=beverageNum;k++){
                    //当前费用至少需要前一次费用的数值
                    dp[i][j][k]=dp[i-1][j][k];
                    /*如果j大于等于breadNum是正常的01背包问题,直接从背包取
                      如果j小于breadNum,仍然从背包取,只是应该从状态为0的位置转移
                      对k这一层的维度也是类似的分析过程*/
                    dp[i][j][k]=Math.min(dp[i][j][k]
                                      ,dp[i-1][Math.max(j-packageSum[i-1][0],0)][Math.max(k-packageSum[i-1][1],0)]
                                      +packageSum[i-1][2]);
                }
            }
        }

        return dp[n][breadNum][beverageNum];
    }
}

3.复杂度分析

  • 时间复杂度:假设输入面包的数目为breadNum,饮料的数目为beverageNum,包装的数量为n,则时间复杂度为
  • 空间复杂度:需要额外大小为的dp数组,所以空间复杂度为

方法二(空间压缩)

1.解题思路

基本思路和方法一相同,另外可以通过滚动数组的方式降低一维空间的开销,同时为了避免重复计算,需要逆序遍历。

动图展示(测试的输入数据为:2,2,[[1,3,12],[1,2,15],[2,1,20]]):
图片说明

2.代码实现

import java.util.*;

public class Solution {
    /**
     * 
     * @param breadNum int整型 
     * @param beverageNum int整型 
     * @param packageSum int整型二维数组 每个一维数组中有三个数,依次表示这个包装里面的面包数量、饮料数量、花费
     * @return int整型
     */
    static final int INF=Integer.MAX_VALUE/2;

    public int minCost (int breadNum, int beverageNum, int[][] packageSum) {
        /*初始化dp数组,第一维表示面包数,
        第二维表示饮料数,整体表示当前最小的花费*/
        int[][] dp=new int[breadNum+1][beverageNum+1];
        for(int i=0;i<=breadNum;i++){
            Arrays.fill(dp[i],INF);
        }
        //不选择任何包装时,花费为0
        dp[0][0]=0;
        int n=packageSum.length;
        for(int i=0;i<n;i++){
            for(int j=breadNum;j>=0;j--){
                for(int k=beverageNum;k>=0;k--){
                    //通过逆序的方式跟新状态,防止重复计算,同时节省了一维空间
                    dp[j][k]=Math.min(dp[j][k]
                                      ,dp[Math.max(j-packageSum[i][0],0)][Math.max(k-packageSum[i][1],0)]
                                      +packageSum[i][2]);
                }
            }
        }

        return dp[breadNum][beverageNum];
    }
}

3.复杂度分析

  • 时间复杂度:假设输入面包的数目为breadNum,饮料的数目为beverageNum,包装的数量为n,则时间复杂度为
  • 空间复杂度:需要额外大小为的dp数组,所以空间复杂度为