动态规划求最大值:
题目描述
小蓝在一个 nn 行 mm 列的方格图中玩一个游戏。
开始时,小蓝站在方格图的左上角,即第 11 行第 11 列。
小蓝可以在方格图上走动,走动时,如果当前在第 rr 行第 cc 列,他不能走到行号比 rr 小的行,也不能走到列号比 cc 小的列。同时,他一步走的直线距离不超过 33。
例如,如果当前小蓝在第 33 行第 55 列,他下一步可以走到第 33 行第 66 列、第 33 行第 77 列、第 33 行第 88 列、第 44 行第 55 列、第 44 行第 66 列、第 44 行第 77 列、第 55 行第 55 列、第 55 行第 66 列、第 66 行第 55 列之一。
小蓝最终要走到第 nn 行第 mm 列。
在图中,有的位置有奖励,走上去即可获得,有的位置有惩罚,走上去就要接受惩罚。奖励和惩罚最终抽象成一个权值,奖励为正,惩罚为负。
小蓝希望,从第 11 行第 11 列走到第 nn 行第 mm 列后,总的权值和最大。请问最大是多少?
输入描述
输入的第一行包含两个整数 n,mn,m,表示图的大小。
接下来 nn 行,每行 mm 个整数,表示方格图中每个点的权值。
其中,1≤n≤100,−104≤权值≤1041≤n≤100,−104≤权值≤104。
输出描述
输出一个整数,表示最大权值和。
输入输出样例
示例 1
输入
3 5
-4 -5 -10 -3 1
7 5 -9 3 -10
10 -2 6 -10 -4
输出
15
运行限制
- 最大运行时间:1s
- 最大运行内存: 128M
实现:
import java.util.Scanner;
public class jump {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n=scan.nextInt();
int m=scan.nextInt();
int[][] arr=new int[n][m];
for(int i=0;i<n;i++) {
for(int j=0;j<m;j++) {
arr[i][j]=scan.nextInt();
}
}
scan.close();
int[][] b=new int[n][m];
b[0][0]=arr[0][0];
for(int i=0;i<n;i++) {
for(int j=0;j<m;j++) {
int c=3;
while(c>0) {
if(i-c>=0) {
b[i][j]=Math.max(b[i][j], b[i-c][j]+arr[i][j]);
}
if(j-c>=0) {
b[i][j]=Math.max(b[i][j], b[i][j-c]+arr[i][j]);
}
c--;
}
}
}
System.out.println(b[n-1][m-1]);
}
}
动态规划求最长子序列
题目描述
L 星球上的生物由蛋蓝质组成,每一种蛋蓝质由一类称为蓝肽的物资首尾连接成一条长链后折叠而成。
生物学家小乔正在研究 L 星球上的蛋蓝质。她拿到两个蛋蓝质的蓝肽序列,想通过这两条蓝肽序列的共同特点来分析两种蛋蓝质的相似性。
具体的,一个蓝肽可以使用 11 至 55 个英文字母表示,其中第一个字母大写,后面的字母小写。一个蛋蓝质的蓝肽序列可以用蓝肽的表示顺序拼接而成。
在一条蓝肽序列中,如果选取其中的一些位置,把这些位置的蓝肽取出,并按照它们在原序列中的位置摆放,则称为这条蓝肽的一个子序列。蓝肽的子序列不一定在原序列中是连续的,中间可能间隔着一些未被取出的蓝肽。
如果第一条蓝肽序列可以取出一个子序列与第二条蓝肽序列中取出的某个子序列相等,则称为一个公共蓝肽子序列。
给定两条蓝肽序列,找出他们最长的那个公共蓝肽子序列的长度。
输入描述
输入两行,每行包含一个字符串,表示一个蓝肽序列。字符串中间没有空格等分隔字符。
其中有 ,两个字符串的长度均不超过 10001000。
输出描述
输出一个整数,表示最长的那个公共蓝肽子序列的长度。
输入输出样例
示例
输入
LanQiaoBei
LanTaiXiaoQiao
输出
2
运行限制
- 最大运行时间:1s
- 最大运行内存: 128M
import java.util.*;
public class lantai {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
//在此输入您的代码...
String str1=scan.nextLine();
String str2=scan.nextLine();
scan.close();
int[] flag1=new int[str1.length()];
int[] flag2=new int[str2.length()];
int c=0;
int m=0,n=0;
for(int i=0;i<str1.length();i++) {
if(str1.charAt(i)>='A'&&str1.charAt(i)<='Z') {
flag1[c]=i;
c++;
m++;
}
}
flag1[c]=str1.length();
c=0;
for(int i=0;i<str2.length();i++) {
if(str2.charAt(i)>='A'&&str2.charAt(i)<='Z') {
flag2[c]=i;
c++;
n++;
}
}
flag2[c]=str2.length();
int[][] dp=new int[m+1][n+1];
for(int i=0;i<m;i++) {
for(int j=0;j<n;j++) {
// System.out.println(str1.substring(flag1[i], flag1[i+1])+" "+str2.substring(flag2[j], flag2[j+1]));
if(str1.substring(flag1[i], flag1[i+1]).equals(str2.substring(flag2[j], flag2[j+1]))) {
dp[i+1][j+1]=Math.max(dp[i+1][j+1], dp[i][j]+1);
}else{
dp[i+1][j+1]=Math.max(dp[i][j+1], dp[i+1][j]);
}
}
}
System.out.println(dp[m][n]);
}
}
上述有两例发生段错误,
import java.util.Scanner;
// 1:无需package
// 2: 类名必须Main, 不可修改
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
//在此输入您的代码...
String str1=scan.nextLine();
String str2=scan.nextLine();
scan.close();
int[] flag1=new int[str1.length()+1];
int[] flag2=new int[str2.length()+1];
int c=0;
int m=0,n=0;
for(int i=0;i<str1.length();i++) {
if(str1.charAt(i)>='A'&&str1.charAt(i)<='Z') {
flag1[c]=i;
c++;
m++;
}
}
flag1[c]=str1.length();
c=0;
for(int i=0;i<str2.length();i++) {
if(str2.charAt(i)>='A'&&str2.charAt(i)<='Z') {
flag2[c]=i;
c++;
n++;
}
}
flag2[c]=str2.length();
int[][] dp=new int[m+1][n+1];
for(int i=1;i<=m;i++) {
for(int j=1;j<=n;j++) {
// System.out.println(str1.substring(flag1[i-1], flag1[i])+" "+str2.substring(flag2[j-1], flag2[j]));
if(str1.substring(flag1[i-1], flag1[i]).equals(str2.substring(flag2[j-1], flag2[j]))) {
dp[i][j]=Math.max(dp[i][j], dp[i-1][j-1]+1);
}else{
dp[i][j]=Math.max(dp[i-1][j], dp[i][j-1]);
}
}
}
System.out.println(dp[m][n]);
}
}
包子凑数:
题目描述
小明几乎每天早晨都会在一家包子铺吃早餐。他发现这家包子铺有 NN 种蒸笼,其中第 ii 种蒸笼恰好能放 AiAi 个包子。每种蒸笼都有非常多笼,可以认为是无限笼。
每当有顾客想买 XX 个包子,卖包子的大叔就会迅速选出若干笼包子来,使得这若干笼中恰好一共有 XX 个包子。比如一共有 3 种蒸笼,分别能放 3、4 和 5 个包子。当顾客想买 11 个包子时,大叔就会选 2 笼 3 个的再加 1 笼 5 个的(也可能选出 1 笼 3 个的再加 2 笼 4 个的)。
当然有时包子大叔无论如何也凑不出顾客想买的数量。比如一共有 3 种蒸笼,分别能放 4、5 和 6 个包子。而顾客想买 7 个包子时,大叔就凑不出来了。
小明想知道一共有多少种数目是包子大叔凑不出来的。
输入描述
第一行包含一个整数 NN (1≤N≤1001≤N≤100)。
以下 N 行每行包含一个整数 AiAi (1≤Ai≤1001≤Ai≤100)。
输出描述
一个整数代表答案。如果凑不出的数目有无限多个,输出 INF。
输入输出样例
示例 1
输入
2
4
5
输出
6
样例说明
凑不出的数目包括:1, 2, 3, 6, 7, 11。
示例 2
输入
2
4
6
输出
INF
样例说明
所有奇数都凑不出来,所以有无限多个
运行限制
- 最大运行时间:1s
- 最大运行内存: 256M
import java.util.*;
public class baozicoushu {
public static int gcd(int a,int b) {
if(b==0) {
return a;
}else {
return gcd(b,a%b);
}
}
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n=scan.nextInt();
int[] a=new int[n+1];
int s;
for(int i=1;i<=n;i++) {
a[i]=scan.nextInt();
}
scan.close();
s=a[1];
for(int i=1;i<=n;i++) {
s=gcd(s,a[i]); //求得最大公约数(所输入每一笼包子数的最大公约数)
}
if(s!=1) { //这些数中包含最大公约数,不互质,例如3,6,9这些数不能组成的数为无穷
System.out.println("INF");
}else{
boolean[] f=new boolean[10001];
f[0]=true;
for(int i=1;i<=n;i++) {
for(int j=0;j<10001;j++) { //标记数组,寻找不能凑成的数字
if(f[j]&&(j+a[i])<10001) {
f[j+a[i]]=true;
}
}
}
int sum=0;
for(int i=1;i<10001;i++) {
if(!f[i]) {
sum++;
}
}
System.out.println(sum);
}
}
}
01背包问题
回溯:
import java.util.*;
public class ZeroAndOne {
public static void main(String[] args) {
int[][] dp=new int[5][9];
int[] w= {0,2,3,4,5}; //物品对应的容量
int[] v= {0,3,4,5,8}; //物品对应的价值
for(int i=1;i<5;i++) {
for(int j=1;j<9;j++) { //j表示背包容量
if(w[i]>j) {
dp[i][j]=dp[i-1][j];
}else {
dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
} //不偷第i-1个物品 偷
}
}
System.out.print(dp[4][8]);
}
}
背包与魔法
问题描述
小蓝面前有 NN 件物品, 其中第 ii 件重量是 WiWi, 价值是 ViVi 。她还有一个背包, 最大承重是 MM 。
小蓝想知道在背包称重范围内, 她最多能装总价值多少的物品?
特别值得一提的是, 小蓝可以使用一个魔法 (总共使用一次), 将一件物品 的重量增加 KK, 同时价值秝倍。(当然小蓝也可以不使用魔法)
输入格式
第一行包含 3 个整数 N、MN、M 和 KK 。
以下 NN 行, 每行两个整数 WiWi 和 ViVi 。
输出格式
一个整数代表答案。
样例输入
3 10 3
5 10
4 9
3 8
样例输出
26
样例说明
选择第二件和第三件物品, 同时对第二件物品使用魔法。
评测用例规模与约定
对于 30%30% 的数据, 1≤N,M,K≤1001≤N,M,K≤100.
对于 100%100% 的数据, 1≤N≤2000,1≤M,K≤10000,0≤Wi,Vi≤100001≤N≤2000,1≤M,K≤10000,0≤Wi,Vi≤10000.
运行限制
语言 | 最大运行时间 | 最大运行内存 |
---|---|---|
C++ | 1s | 256M |
C | 1s | 256M |
Java | 3s | 256M |
Python3 | 5s | 256M |
状态转移方程:
若不使用魔法,也就是j=0的时候的情况,跟01背包的模板一样,
dp[j][0] = Math.max(dp[j][0],dp[j-w[i]][0]+v[i])
若使用魔法,(要保证j的大小符合条件)
有两种情况,这次的物品使用魔法或者之前其中的一个物品使用魔法
1.若这次物品使用魔法,即Math.max(dp[j-w[i]-k][0]+2*v[i]
2.若之前其中的一个物品使用魔法,即dp[j-w[i]][1]+v[i]
合起来就是dp[j][1] = Math.max(dp[j][1],Math.max(dp[j-w[i]-k][0]+2*v[i],dp[j-w[i]][1]+v[i]));
import java.util.*;
public class PacageAndMagin {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int m = scan.nextInt();
int k = scan.nextInt();
int[] w = new int[n+1];
int[] v = new int[n+1];
w[0]=0;
v[0]=0;
for(int i=1;i<=n;i++) {
w[i]=scan.nextInt();
v[i]=scan.nextInt();
}
scan.close();
// int[][] dp=new int[n+1][m+1];
// for(int i=1;i<n+1;i++) {
// for(int j=1;j<m+1;j++) {
// if(j<w[i]) {
// dp[i][j]=dp[i-1][j];
// }else {
// System.out.println(i+" "+j);
// dp[i][j]=Math.max(dp[i-1][j], dp[i-1][j-w[i]]+v[i]);
// System.out.println(i+" "+j);
// }
// }
// }
int[][] dp=new int[m+1][2];
for(int i=1;i<n+1;i++) {
for(int j=m;j>=w[i];j--) {
if(j>=k+w[i]) {//使用药水或者之前使用药水
dp[j][0]=Math.max(dp[j][0],dp[j-w[i]][0]+v[i] );
//没有使用药水的最优组合 不装这个物品当前容量的最优组合,装下当前物品的最优组合
dp[j][1]=Math.max(dp[j][1], Math.max(dp[j-w[i]-k][0]+2*v[i], dp[j-w[i]][1]+v[i]));
// 之前使用药水但这次不装入物品的最优组合 这次使用药水并装入这个物品的最优组合,这次不用使药水但之前使用药水的最优组合
}else {
dp[j][0]=Math.max(dp[j][0], dp[j-w[i]][0]+v[i]);
}
}
}
System.out.println(Math.max(dp[m][0], dp[m][1]));
}
}