一、乘积最大(简单、线性DP)
考虑限制条件:数字串的长度N 和 乘号个数K。用dp[i][j]来表示字符串中前 i 个数,在使用k个乘号的情况下的最大乘积。
那么dp[i][0] = 这个字符串构成的数,我们要找的答案=dp[N - 1][K - 1]。
dp[i][j]怎么得到?我们可以对最后一个乘号:
考虑最后一个乘号,前面还有 j - 1 个乘号,这 j - 1 个乘号,至少需要 j 个数来乘,所以最后一个乘号的枚举位置,应该从 j 开始,枚举到 i - 1的位置,因为最后一个乘号至少需要一位数来乘。
int max = INT_MIN;
// 迭代所有子集
for (int k = j; k <= i - 1; k++) {
// 字符串长度为 i 个字符,放 j 个乘号的最大乘积
// k 代表最后一个乘号放的位置,前j - 1个乘号,需要j个字符去乘(至少)
// 那么最后一个元素可以放在
max = Math.max(max, dp[k][j - 1] * getCnt(k + 1, i));
}
import java.util.*;
public class Main {
public static String str = "";
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int k = scan.nextInt();
str = scan.next();
// dp[i][j],前 i 个字符放 j 个乘号的最大乘积
int[][] dp = new int[n + 1][k + 1];
for (int i = 1; i <= n; i++) {
// 无乘号的值就是其本身
dp[i][0] = getCnt(1, i);
}
// 前 i 个字符
for (int i = 1; i <= n; i++) {
// 放 j 个乘号,注意如果有 i 个字符,最多可以放 i - 1个乘号
for (int j = 1; j <= i - 1 && j <= k; j++) {
// 遍历所有子集情况:最后一个乘号放在哪里?
int max = Integer.MIN_VALUE;
// 这个kk,枚举的是剩余的j - 1个乘号放在多少个字符之间(至少是j个字符)
for (int kk = j; kk <= i - 1; kk++) {
max = Math.max(max, dp[kk][j - 1] * getCnt(kk + 1, i));
}
dp[i][j] = max;
}
}
System.out.println(dp[n][k]);
}
// 计算当前区间字符的数字大小
public static int getCnt(int left, int right) {
int res = 0;
for (int i = left - 1; i <= right - 1; i++) {
res = res * 10 + str.charAt(i) - '0';
}
return res;
}
}
二、方格取数(简单)
两个人同时走,四维DP(我都Σ(っ °Д °;)っ)
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int[][] num = new int[n + 1][n + 1];
// A 1,1 B N,N
int a, b, c;
while (true) {
a = scan.nextInt();b = scan.nextInt();c = scan.nextInt();
if (a == 0) break;
num[a][b] = c;
}
// 两个人从起点走到终点
int[][][][] dp = new int[n + 1][n + 1][n + 1][n + 1];
// dp[i][j][k][l] : A走到i,j,B走到k,l的最大路径和
// A、B均可向下向右,所以会产生4种情况
// A下B下,A下B右,A右B下,A右B右
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
for (int k = 1; k <= n; k++) {
for (int l = 1; l <= n; l++) {
// 取四种情况的最大值
dp[i][j][k][l] = Math.max(Math.max(dp[i - 1][j][k - 1][l], dp[i - 1][j][k][l - 1]), Math.max(dp[i][j - 1][k - 1][l], dp[i][j - 1][k][l - 1])) + num[i][j] + num[k][l];
if (i == k && j == l) {
// 走的是同一格,那就要减回去
dp[i][j][k][l] -= num[i][j];
}
}
}
}
}
System.out.println(dp[n][n][n][n]);
}
}
三、求先序排列(简单)
二叉树…还在磨练中~待更
四、集合运算(简单)
直接模拟即可
import java.util.*;
public class Main {
public static void main(String[] args) {
// 交集
int[] numA = new int[1000];
// 并集
int[] numB = new int[2000];
// 补集
int[] numC = new int[1000];
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int[] A = new int[n];
HashSet<Integer> setA = new HashSet<>();
// B在A中的余集中的所有元素:A中剔除所有B中元素
// 交集、并集、补集下标
int a = 0, b = 0, c = 0;
for (int i = 0; i < n; i++) {
A[i] = scan.nextInt();
setA.add(A[i]);
// 并集,A中元素一定都在
numB[b++] = A[i];
}
int m = scan.nextInt();
int[] B = new int[m];
HashSet<Integer> setB = new HashSet<>();
for (int i = 0; i < m; i++) {
B[i] = scan.nextInt();
setB.add(B[i]);
// 交集
if (setA.contains(B[i])) {
numA[a++] = B[i];
}
// 并集,B中元素一定都在
numB[b++] = B[i];
}
// 求B在A中的余集
for (int i = 0; i < n; i++) {
if (setB.contains(A[i])) {
continue;
}
numC[c++] = A[i];
}
if (a != 0) {
Arrays.sort(numA, 0, a);
for (int i = 0; i < a; i++) {
System.out.printf("%d ", numA[i]);
}
System.out.println();
}
Arrays.sort(numB, 0, b);
for (int i = 0; i < b; i++) {
if (i > 0 && numB[i] == numB[i - 1]) continue;
System.out.printf("%d ", numB[i]);
}
System.out.println();
if (c != 0) {
Arrays.sort(numC, 0, c);
for (int i = 0; i < c; i++) {
System.out.printf("%d ", numC[i]);
}
}
}
}