23.分组背包和完全背包(动态规划专题)

23.分组背包和完全背包

分组背包、完全背包

前置知识: 讲解067、讲解068- 二维动态规划及其空间压缩技巧 讲解073- 01背包、有依赖的背包 【必备】课程的动态规划大专题从讲解066开始,建议从头开始学习会比较系统

分组背包:多个物品分组,每组只能取1件 每一组的物品都可能性展开就可以了。注意时间复杂度不会升阶,0(物品数量*背包容量)

完全背包:与01背包的区别仅在于 每种商品可以选取无限次。时间复杂度0(物品数量*背包容量)

P1757 通天之分组背包 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

这道题通过了百分之99,最后百分之1我也没搞懂错在哪,欢迎指点

题目描述

自 0101 背包问世之后,小 A 对此深感兴趣。一天,小 A 去远游,却发现他的背包不同于 0101 背包,他的物品大致可分为 kk 组,每组中的物品相互冲突,现在,他想知道最大的利用价值是多少。

输入格式

两个数 m,nm,n,表示一共有 nn 件物品,总重量为 mm

接下来 nn 行,每行 33 个数 ai,bi,cia**i,b**i,c**i,表示物品的重量,利用价值,所属组数。

输出格式

一个数,最大的利用价值。

输入输出样例

输入 #1复制

45 3
10 10 1
10 5 1
50 400 2

输出 #1复制

10
static int c,v,zu;
public static void main(String[] args) throws IOException {
    BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
    String[] s = bf.readLine().split(" ");
    int m = Integer.parseInt(s[0]);
    int n = Integer.parseInt(s[1]);
    //代表每一组  map [i]
    int [][]arr = new int[n+1][3];
    int zunums = 1; //记录组的数量
    int zunumber = 0;// 记录组的编号
    for (int i = 1;i<=n;i++){
        s = bf.readLine().split(" ");
        arr[i][0] = Integer.parseInt(s[0]); //cost
        arr[i][1]  = Integer.parseInt(s[1]);//val
        arr[i][2]  = Integer.parseInt(s[2]);//分组
        if(zunumber!=0&&arr[i][2]!=zunumber){
            zunums++;
        }
        zunumber = arr[i][2];
    }
    //按组号进行排序
    Arrays.sort(arr, new Comparator<int[]>() {
        @Override
        public int compare(int[] o1, int[] o2) {
            return o1[2]-o2[2];
        }
    });
    long [][]dp = new long[zunums+1][m+1];
    for (int i = 1,start = 1,end = 2;start<=n;i++){
        while(end<=n&&arr[start][2]==arr[end][2]){
            end++;
        }
        //至此 start - end(不包扩end)就变成一个组的数据了
        for (int j = 0;j<=m;j++){
            //这一组都不要
            dp[i][j] =  dp[i-1][j];
            for (int k = start;k<end;k++){
                if (j-arr[k][0]>=0){
                    dp[i][j] = Math.max(dp[i][j],dp[i-1][j-arr[k][0]]+arr[k][1]);
                }
            }
        }
        start = end++;
    }
    PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
    out.print(dp[zunums][m]);
    out.close();
}

2218. 从栈中取出 K 个硬币的最大面值和

已解答

困难

提示

一张桌子上总共有 n 个硬币 。每个栈有 正整数 个带面值的硬币。

每一次操作中,你可以从任意一个栈的 顶部 取出 1 个硬币,从栈中移除它,并放入你的钱包里。

给你一个列表 piles ,其中 piles[i] 是一个整数数组,分别表示第 i 个栈里 从顶到底 的硬币面值。同时给你一个正整数 k ,请你返回在 恰好 进行 k 次操作的前提下,你钱包里硬币面值之和 最大为多少

示例 1:

img

输入:piles = [[1,100,3],[7,8,9]], k = 2
输出:101
解释:
上图展示了几种选择 k 个硬币的不同方法。
我们可以得到的最大面值为 101 。
static List<Integer> cur;
public static int maxValueOfCoins(List<List<Integer>> piles, int k) {
    int n = piles.size();
    int [][] dp = new int[n+1][k+1];
    int max = 0;
    for (int i = 0;i<n;i++){
        max = Math.max(max,piles.get(i).size());
    }
    int [][] presum = new int[n][max+1];
    for (int i = 0;i<n;i++){
        int sum = 0;
        for (int j = 0;j<piles.get(i).size();j++){
            sum+=piles.get(i).get(j);
            presum[i][j] = sum;
        }
    }
    for (int i = 1;i<=n;i++){
        cur = piles.get(i-1);
        for (int j = 0;j<=k;j++){
            //来到第i个栈,1.这个栈一个也不拿,
            dp[i][j] = dp[i-1][j];
            //2.这个栈拿1...Math.min()个
            int min = Math.min(cur.size(),j);
            for (int l = 0;l<min;l++){
                dp[i][j] = Math.max(dp[i][j],dp[i-1][j-l-1]+presum[i-1][l]);
            }
        }
    }
    return dp[n][k];
}

完全背包模板题

P1616 疯狂的采药 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

这里使用了空间压缩,不然空间不能通过

public static void main(String[] args) throws IOException {
    BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
    String[] s = bf.readLine().split(" ");
    //总时间
    int time = Integer.parseInt(s[0]);
    //种类
    int n = Integer.parseInt(s[1]);
    int [] cost = new int[n+1];
    int [] value = new int[n+1];
    for (int i = 1;i<n+1;i++){
        s = bf.readLine().split(" ");
        cost[i] = Integer.parseInt(s[0]);
        value[i] = Integer.parseInt(s[1]);
    }
    long []dp = new long[time+1];
    //当前从第一种草药开始,要或者不要
    //先填好第一种草药
    for (int i = 1;i<=n;i++){
        //来到第i种草药
        for (int j = cost[i];j<=time;j++){
            //如果要当前草药
            dp[j] = Math.max(dp[j-cost[i]]+value[i],dp[j]);
        }
    }
    PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
    out.println(dp[time]);
    out.flush();
    out.close();
    bf.close();
}

10. 正则表达式匹配

已解答

困难

这道题需要的是字符串完全匹配而不是部分匹配!

下面一题和这题类似,觉得难可以先做下面一个

给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.''*' 的正则表达式匹配。

  • '.' 匹配任意单个字符

  • '*' 匹配零个或多个前面的那一个元素

所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。

示例 1:

输入:s = "aa", p = "a"
输出:false
解释:"a" 无法匹配 "aa" 整个字符串。

示例 2:

输入:s = "aa", p = "a*"
输出:true
解释:因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。
static int [][]dp;
public static boolean isMatch(String s, String p) {
    char[] chars = s.toCharArray();
    char[] charp = p.toCharArray();
    dp = new int[chars.length+1][charp.length+1];
    dp[chars.length][charp.length]=1;
    return process(chars,charp,0,0);
}
​
private static boolean process(char[] chars, char[] charp, int indexs,int indexp) {
    if (dp[indexs][indexp]!=0){
        return dp[indexs][indexp]==1;
    }
    //如果匹配到字符完结 直接return true
    if (indexs==chars.length){
        if (indexp==charp.length){
            dp[indexs][indexp] = 1;
            return true;
        }else{
            //如果p字符串还有 字符 判断后面是否是字母+*结尾的,是的话也可以当作没有字符,反之返回false
            dp[indexs][indexp] = indexp+1<charp.length&&charp[indexp+1]=='*'&&process(chars,charp,indexs,indexp+2) ? 1:2;
            return dp[indexs][indexp]==1;
        }
    }
    //开始匹配
    //后面一个是’*‘
    if (indexp+1<charp.length&&charp[indexp+1]=='*'){
        //当前就[indexp]就可以选择从0...到n个,完全背包模板
        boolean p1 = process(chars,charp,indexs,indexp+2);
        boolean p2 = process(chars,charp,indexs+1,indexp)&&(charp[indexp]==chars[indexs]||charp[indexp]=='.');
        dp[indexs][indexp]=p1||p2 ? 1:2;
        return dp[indexs][indexp]==1;
    }else {
        if (indexp<charp.length&&(chars[indexs]==charp[indexp]||charp[indexp]=='.')){
            dp[indexs][indexp] =process(chars,charp,indexs+1,indexp+1) ? 1:2;
            return dp[indexs][indexp]==1;
        }
    }
    return false;
}

44. 通配符匹配

和上一题类似,但是这个更简单一些

已解答

困难

给你一个输入字符串 (s) 和一个字符模式 (p) ,请你实现一个支持 '?''*' 匹配规则的通配符匹配:

  • '?' 可以匹配任何单个字符。

  • '*' 可以匹配任意字符序列(包括空字符序列)。

判定匹配成功的充要条件是:字符模式必须能够 完全匹配 输入字符串(而不是部分匹配)。

示例 1:

输入:s = "aa", p = "a"
输出:false
解释:"a" 无法匹配 "aa" 整个字符串。
static int [][]dp;
public  static boolean isMatch(String s, String p) {
    char[] chars = s.toCharArray();
    char[] charp = p.toCharArray();
    dp = new int[chars.length+1][charp.length+1];
    dp[chars.length][charp.length]=1;
    return process(chars,charp,0,0);
}
​
private static boolean process(char[] chars, char[] charp, int indexs,int indexp) {
    if (dp[indexs][indexp]!=0){
        return dp[indexs][indexp]==1;
    }
    //如果匹配到字符完结 直接return true
    if (indexp==charp.length){
        if (indexs==chars.length){
            dp[indexs][indexp] = 1;
            return true;
        }else{
           return false;
        }
    }
    //开始匹配
    //后面一个是’*‘
    if (charp[indexp]=='*'){
        //当前就[indexp]就可以选择从0...到n个,完全背包模板
        boolean p1 = process(chars,charp,indexs,indexp+1);
        boolean p2 = indexs < chars.length && process(chars, charp, indexs + 1, indexp);
        dp[indexs][indexp]=p1||p2 ? 1:2;
        return dp[indexs][indexp]==1;
    }else {
        if (indexs<chars.length&&(chars[indexs]==charp[indexp]||charp[indexp]=='?')){
            dp[indexs][indexp] =process(chars,charp,indexs+1,indexp+1) ? 1:2;
            return dp[indexs][indexp]==1;
        }
    }
    return false;
}

[P2918 USACO08NOV] Buying Hay S - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

import java.io.*;
import java.nio.Buffer;
import java.util.Arrays;
​
public class Main {
​
​
    public static void main(String[] args) throws IOException {
        BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
​
        String [] s = bf.readLine().split(" ");
        //公司数目
        int N = Integer.parseInt(s[0]);
        //需要的榜数
        int H = Integer.parseInt(s[1]);
        int [] cost = new int[N+1];
        int [] value = new int[N+1];
        int max = 0;
        for (int i = 1;i<=N;i++){
            s = bf.readLine().split(" ");
            value[i] = Integer.parseInt(s[0]);
            cost[i] = Integer.parseInt(s[1]);
            max = Math.max(value[i],max);
        }
​
​
        int [][]dp = new int[N+1][max+H];
        Arrays.fill(dp[0],Integer.MAX_VALUE);
        dp[0][0] = 0;
        //dp[i][j]代表来到第i家公司,购买价值j磅的甘草的最小消费
        for (int i = 1;i<N+1;i++){
            //来到第i家公司挑选干草
            for (int j = 0;j<dp[0].length;j++){
                //一份都不买
                dp[i][j] = dp[i-1][j];
                //能买几份买几份
                if(j-value[i]>=0&&dp[i][j-value[i]]!=Integer.MAX_VALUE){
                    dp[i][j]=Math.min(dp[i][j],dp[i][j-value[i]]+cost[i]);
                }
            }
        }
        PrintWriter out=  new PrintWriter(new OutputStreamWriter(System.out));
        int min = Integer.MAX_VALUE;
        for (int i = H;i<H+max;i++){
            min = Math.min(min,dp[N][i]);
        }
        out.print(min);
        out.flush();
        out.close();
        bf.close();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值