动态规划习题
DP习题
243. 快递运输【买手办问题!!!】
import java.util.Scanner;
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String[] split = in.nextLine().trim().split(",");
int[] arr = new int[split.length];
for (int i = 0; i < arr.length; i++) {
arr[i] = Integer.parseInt(split[i]);
}
int size = in.nextInt();
int[][] dp = new int[arr.length + 1][size + 1];
for (int i = 1; i < dp.length; i++) {
for (int j = size; j >= 0; j--) {
if (j >= arr[i - 1]){
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - arr[i - 1]] + 1);
}else {
dp[i][j] = dp[i - 1][j];
}
}
}
System.out.println(dp[arr.length][size]);
}
}
Melon的难题【01背包问题中“装满背包的最少物品数问题】----> 背包从小到大
注意初始化问题,第一行除了第一个都要赋值最大值【物品个数】!!!
import java.util.Scanner;
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int count = in.nextInt();
int[] arr = new int[count];
int sum = 0;
for (int i = 0; i < arr.length; i++) {
arr[i] = in.nextInt();
sum += arr[i];
}
if (sum % 2 != 0){
System.out.println(-1);
return;
}
int[][] dp = new int[count + 1][sum / 2 + 1]; // 装满背包所需最小物品数量
Arrays.fill(dp[0], count);
dp[0][0] = 0;
for (int i = 1; i < dp.length; i++) {
for (int j = 0; j <= sum / 2; j++) {
if (j >= arr[i - 1]){ // 注意等号
dp[i][j] = Math.min(dp[i - 1][j], dp[i -1][j - arr[i - 1]] + 1); // 求个数
}else {
dp[i][j] = dp[i - 1][j];
}
}
}
//// 如果装满背包的最少物品数为n, 则说明没有平分方案,因为n个雨花石的重量之和为sumV,而背包的承重是bag = sumV / 2
if (dp[dp.length - 1][sum / 2] == count){ // dp[i][j]:容量为 j 的背包最少能用 i 个物品装满`
System.out.println(-1);
return;
}
System.out.println(dp[dp.length - 1][sum / 2]);
}
}
打印 DP 数组如下:
代表团坐车
一维滚动数组
import java.util.Scanner;
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String[] split = in.nextLine().split(",");
int[] arr = new int[split.length];
for (int i = 0; i < arr.length; i++) {
arr[i] = Integer.parseInt(split[i]);
}
int count = in.nextInt();
int[] dp = new int[count + 1];
dp[0] = 1;
for (int i : arr) {
for (int j = count; j >= 0; j--) {
if (j >= i){
dp[j] = dp[j] + dp[j - i];
}
}
}
System.out.println(dp[count]);
}
}
二维
import java.util.Scanner;
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String[] split = in.nextLine().split(",");
int[] arr = new int[split.length];
for (int i = 0; i < arr.length; i++) {
arr[i] = Integer.parseInt(split[i]);
}
int count = in.nextInt();
int[][] dp = new int[arr.length + 1][count + 1];
dp[0][0] = 1;
for (int i = 1; i <= dp.length - 1 ; i++) {
for (int j = count; j >= 0; j--) {
if (j >= arr[i - 1]){
dp[i][j] = dp[i - 1][j] + dp[i - 1][j - arr[i -1]];
}else {
dp[i][j] = dp[i - 1][j];
}
}
}
System.out.println(dp[dp.length - 1][count]);
}
}
192. 跳格子【打家劫舍】
import java.util.Scanner;
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String[] split = in.nextLine().split(" ");
int[] arr = new int[split.length];
for (int i = 0; i < arr.length; i++) {
arr[i] = Integer.parseInt(split[i]);
}
int[] dp = new int[arr.length + 2];
for (int i = 2; i < dp.length; i++) {
// 不要这个
// 要这个:那么i - 1 就不要了,dp[i- 2] + 当前
dp[i] = Math.max(dp[i - 1], dp[i - 2] + arr[i - 2]);
}
System.out.println(dp[dp.length - 1]);
}
}
193. 跳格子2【打家劫舍2】
这道题中的房屋是首尾相连的,第一间房屋和最后一间房屋相邻,因此第一间房屋和最后一间房屋不能在同一晚上偷窃
import java.util.Scanner;
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String[] split = in.nextLine().split(" ");
int[] arr = new int[split.length];
for (int i = 0; i < arr.length; i++) {
arr[i] = Integer.parseInt(split[i]);
}
int[] arr1 = Arrays.copyOfRange(arr, 0, arr.length - 1);
int[] arr2 = Arrays.copyOfRange(arr, 1, arr.length);
System.out.println(Math.max(getMax(arr1), getMax(arr2)));
}
public static int getMax(int[] arr){
int[] dp = new int[arr.length + 2];
for (int i = 2; i < dp.length; i++) {
dp[i] = Math.max(dp[i -1], dp[i -2] + arr[i - 2]);
}
return dp[dp.length - 1];
}
}
LeetCode 337 打家劫舍Ⅲ【树型DP】
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int rob(TreeNode root) {
// 1. 不要当前
// 2. 要当前的
int[] result = getResult(root);
return Math.max(result[0], result[1]);
}
public int[] getResult(TreeNode root){
if (root == null){
return new int[]{0, 0};
}
int[] left = getResult(root.left);
int[] right = getResult(root.right);
int val1 = Math.max(left[0], left[1]) + Math.max(right[0], right[1]); // 不要当前的,它的子节点不一定要 ===> 取最大值即可
int val2 = root.val + left[0] + right[0]; // 偷 cur ===> 那么左右就不能要了
return new int[]{val1, val2};
}
}
通过软盘拷贝文件
import java.util.Scanner;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
static int N;
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
N = Integer.parseInt(in.nextLine());
int[] arr = new int[N];
for (int i = 0; i < N; i++) {
arr[i] = Integer.parseInt(in.nextLine());
}
int target = 1474560 / 512;
int[][] dp = new int[N + 1][target + 1];
for (int i = 1; i <= N; i++) {
for (int j = target; j >= 0; j--) {
int weight = (int) Math.ceil(arr[i - 1] / 512.0);
int value = arr[i - 1];
if (j >= weight){
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight] + value);
}else {
dp[i][j] = dp[i - 1][j];
}
}
}
System.out.println(dp[N][target]);
}
}
字符串加密
import java.util.Scanner;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
long[] arr = new long[50];
arr[0] = 1;
arr[1] = 2;
arr[2] = 4;
for (int i = 3; i < arr.length; i++) {
arr[i] = arr[i - 1] + arr[i - 2] + arr[i - 3];
}
Scanner in = new Scanner(System.in);
int count = Integer.parseInt(in.nextLine()); // 多少行输入数据
for (int i = 0; i < count; i++) {
String temp = "";
String str = in.nextLine(); // 只含有小写字母(长度 <= 50)
for (int j = 0; j < str.length(); j++) {
char c = str.charAt(j);
if (c - 'a' + arr[j] <= 25){
c = (char) (c + arr[j]);
}else {
c = (char) ('a' + ((c - 'a' + arr[j]) % 26));
}
temp = c + "";
sb.append(temp);
}
sb.append("\r\n");
}
System.out.println(sb.toString().trim());
}
}
数列描述
import java.util.Scanner;
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int count = in.nextInt();
String[] dp = new String[count + 1];
dp[0] = "1";
for (int i = 1; i < dp.length; i++) {
String s = dp[i - 1] + "";
StringBuilder sb = new StringBuilder();
StringBuilder res = new StringBuilder();
for (int j = 0; j < s.length(); j++) {
sb.append(s.charAt(j));
if (j + 1 < s.length() && s.charAt(j) == s.charAt(j + 1)){ // 下一个和现在相同就一直 continue
continue;
}
res.append(sb.length() + sb.substring(0, 1));
sb.delete(0, sb.length()); // 清空 sb
}
dp[i] = res.toString();
}
System.out.println(dp[dp.length - 1]);
}
}
猴子爬山
import java.util.Scanner;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int count = Integer.parseInt(in.nextLine());
long[] dp = new long[count + 1];
dp[0] = 1;
dp[1] = 1;
dp[2] = 1;
for (int i = 3; i < dp.length; i++) {
dp[i] = dp[i - 1] + dp[i - 3];
}
System.out.println(dp[count]);
}
}
翻牌求最大分【玩牌高手】
import java.util.Scanner;
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String[] split = in.nextLine().split(",");
int[] arr = new int[split.length];
for (int i = 0; i < arr.length; i++) {
arr[i] = Integer.parseInt(split[i]);
}
int[] dp = new int[arr.length + 3];
for (int i = 3; i < dp.length; i++) {
dp[i] = Math.max(dp[i - 3], dp[i - 1] + arr[i - 3]);
}
System.out.println(dp[dp.length - 1]);
}
}
数组连续和【前缀和】
import java.util.Scanner;
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int count = in.nextInt();
int target = in.nextInt();
int[] arr = new int[count];
for (int i = 0; i < arr.length; i++) {
arr[i] = in.nextInt();
}
long res = 0;
int[] sum = new int[arr.length + 1];
for (int i = 1; i < sum.length; i++) {
sum[i] = sum[i - 1] + arr[i - 1];
if (sum[i] < target){ // 1. 当前【最长的】不可
continue;
}
res += 1; // 2. 当前可
int cur = i - 1;
// 所有数都是整数,那么 sum[] 数组是递增的
// 看前缀和,如果不符合就一直向前推进,如果符合了:说明有 cur 个符合规定
while (cur >= 1 && sum[i] - sum[cur] < target){
cur--;
}
res += cur;
}
System.out.println(res);
}
}
最大矩阵和
import java.util.Scanner;
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
static int row;
static int col;
static int[][] arr;
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
row = in.nextInt();
col = in.nextInt();
arr = new int[row][col];
int res = Integer.MIN_VALUE;
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
arr[i][j] = in.nextInt();
}
}
for (int i = 0; i < row; i++) {
// 单行
res = Math.max(res, getMax(arr[i]));
for (int j = i + 2; j <= row; j++) { // 多行
int[][] temp = Arrays.copyOfRange(arr, i, j);
int[] col = getCol(temp);
int max = getMax(col);
res = Math.max(res, max);
}
}
System.out.println(res);
}
public static int getMax(int[] arr){
int[] dp = new int[arr.length + 1];
int max = Integer.MIN_VALUE;
for (int i = 1; i < dp.length; i++) {
// 有用才要
dp[i] = Math.max(dp[i - 1], 0) + arr[i - 1];
max = Math.max(max, dp[i]);
}
return max;
}
public static int[] getCol(int[][] arr){
int row = arr.length;
int col = arr[0].length;
int[] temp = new int[col];
for (int i = 0; i < col; i++) {
for (int j = 0; j < row; j++) {
temp[i] += arr[j][i];
}
}
return temp;
}
}
字符串划分【先求 sum:sum1, sum2】===> 返回被砍掉的 2 个
import java.util.Scanner;
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
static int[] arr;
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String s = in.nextLine();
arr = new int[s.length()];
for (int i = 0; i < arr.length; i++) {
arr[i] = s.charAt(i);
}
int sum = 0;
for (int i : arr) {
sum += i;
}
int l = 0;
int r = arr.length - 1;
int sum1 = arr[0];
int sum2 = arr[arr.length - 1];
while (l < r){
if (sum1 == sum2){
if (sum - sum1 - sum2 - arr[l + 1] - arr[r - 1] == sum1){
System.out.println((l + 1) + "," + (r - 1)); // 返回被砍掉的 2 个
return;
}else {
l++;
r--;
sum1 += arr[l];
sum2 += arr[r];
}
}else {
if (sum1 < sum2){
l++;
sum1 += arr[l];
}else {
r--;
sum2 += arr[r];
}
}
}
System.out.println("0,0");
}
}
数字游戏【前缀和】
import java.util.Scanner;
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
while (in.hasNextLine()){
String s = in.nextLine();
if ("".equals(s)){
break;
}
String[] split = s.split(" ");
int len = Integer.parseInt(split[0]);
int target = Integer.parseInt(split[1]);
int[] arr = new int[len];
String[] temp = in.nextLine().split(" ");
for (int i = 0; i < arr.length; i++) {
arr[i] = Integer.parseInt(temp[i]);
}
System.out.println(Judge(arr, target));
}
}
public static int Judge(int[] arr, int target){
int[] sum = new int[arr.length + 1];
List<Integer> list = new ArrayList<>();
for (int i = 1; i < sum.length; i++) {
sum[i] = sum[i - 1] + arr[i - 1];
if (sum[i] % target == 0){
return 1;
}
if (list.size() != 0){
for (Integer integer : list) {
if ((sum[i] - integer) % target == 0){
return 1;
}
}
}
list.add(sum[i]);
}
return 0;
}
}
查询接口成功率最优时间段【由于求最长的,所以左侧一直是从 0 开始推进就好了!!! 找到就 break】
import java.util.Scanner;
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int minAverageLost = Integer.parseInt(in.nextLine());
String[] split = in.nextLine().split(" ");
int[] arr = new int[split.length];
for (int i = 0; i < arr.length; i++) {
arr[i] = Integer.parseInt(split[i]);
}
List<Time> list = new ArrayList<>();
int[] sum = new int[arr.length + 1];
for (int i = 1; i < sum.length; i++) {
sum[i] = sum[i - 1] + arr[i - 1];
int index = 0;
while (index < i){
int temp = (int) Math.ceil((sum[i] - sum[index]) / (double)(i - index));
if (temp <= minAverageLost){
list.add(new Time(index, i - 1)); // 坐标从 1 开始, 减去 1
break;
}
index++;
}
}
Collections.sort(list, new Comparator<Time>() {
@Override
public int compare(Time o1, Time o2) {
int len1 = o1.right - o1.left;
int len2 = o2.right - o2.left;
if (len1 != len2){
return len2 - len1; // 1. 长度大的在前
}
return o1.left - o2.left; // 长度相同时,left 优先
}
});
if (list.size() == 0){
System.out.println("NULL");
return;
}
int len = list.get(0).right - list.get(0).left;
StringBuilder res = new StringBuilder();
for (Time time : list) {
if (time.right - time.left == len){
res.append(time.left + "-" + time.right + " ");
}
}
System.out.println(res.toString().trim());
}
}
class Time{
int left;
int right;
public Time(int left, int right) {
this.left = left;
this.right = right;
}
}
高效的任务规划【按照运行时间降序排序】
import java.util.Scanner;
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int total = Integer.parseInt(in.nextLine());
for (int i = 0; i < total; i++) {
int count = Integer.parseInt(in.nextLine());
List<Machine> list = new ArrayList<>();
for (int j = 0; j < count; j++) {
String s = in.nextLine();
String[] split = s.split(" ");
int time1 = Integer.parseInt(split[0]);
int time2 = Integer.parseInt(split[1]);
list.add(new Machine(time1, time2));
}
int config_time = 0;
int res = 0;
Collections.sort(list, (o1, o2) -> {
return o2.time2 - o1.time2; // 运行时间长的先来
});
for (Machine machine : list) {
config_time += machine.time1;
int time = config_time + machine.time2;
res = Math.max(res, time);
}
System.out.println(res);
}
}
}
class Machine{
int time1;
int time2;
public Machine(int time1, int time2) {
this.time1 = time1;
this.time2 = time2;
}
}