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:
输入: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(); } }