文章目录
- 例题列表
- 1015. 摘花生
- 1018. 最低通行费
- 1027. 方格取数(两条路径同时走)⭐⭐⭐⭐⭐
- 275. 传纸条(转换成 两条路径同时走)
例题列表
1015. 摘花生
https://www.acwing.com/problem/content/1017/
状态要么从左转移过来,要么从上转移过来。
import java.io.BufferedInputStream;
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sin = new Scanner(new BufferedInputStream(System.in));
int t = sin.nextInt();
while (t-- != 0) {
int r = sin.nextInt(), c = sin.nextInt();
int[][] g = new int[r][c];
for (int i = 0; i < r; ++i) {
for (int j = 0; j < c; ++j) {
g[i][j] = sin.nextInt();
}
}
for (int i = 0; i < r; ++i) {
for (int j = 0; j < c; ++j) {
int pre = 0;
if (i > 0) pre = Math.max(pre, g[i - 1][j]);
if (j > 0) pre = Math.max(pre, g[i][j - 1]);
g[i][j] += pre;
}
}
System.out.println(g[r - 1][c - 1]);
}
}
}
1018. 最低通行费
https://www.acwing.com/problem/content/1020/
不要被题目的商人必须在
(
2
N
−
1
)
(2N−1)
(2N−1) 个单位时间穿越出去。
其实就是说商人必须往右走或者往下走。
import java.io.BufferedInputStream;
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sin = new Scanner(new BufferedInputStream(System.in));
int n = sin.nextInt();
int[][] a = new int[n][n];
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
a[i][j] = sin.nextInt();
}
}
for (int i = 1; i < n; ++i) a[i][0] += a[i - 1][0];
for (int i = 1; i < n; ++i) a[0][i] += a[0][i - 1];
for (int i = 1; i < n; ++i) {
for (int j = 1; j < n; ++j) {
a[i][j] += Math.min(a[i - 1][j], a[i][j - 1]);
}
}
System.out.println(a[n - 1][n - 1]);
}
}
dp 数组和原始数据数组可以复用。
1027. 方格取数(两条路径同时走)⭐⭐⭐⭐⭐
https://www.acwing.com/problem/content/1029/
第一想法是贪心的,先走一遍 dp,再走一遍 dp。但是后来发现不对,暂时最优未来不一定最优
。
让两个路径同时移动,无非就是 4 种情况,就对应着 4 种状态转移。
k 表示走了几步,i1 和 i2 是两个路径到达的横坐标。
import java.io.BufferedInputStream;
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sin = new Scanner(new BufferedInputStream(System.in));
int n = sin.nextInt();
int[][] m = new int[n + 1][n + 1];
while (true) {
int a = sin.nextInt(), b = sin.nextInt(), c = sin.nextInt();
if (a == 0 && b == 0 && c == 0) break;
m[a][b] = c;
}
int[][][] dp = new int[2 * n + 1][n + 1][n + 1];
for (int k = 2; k <= n + n; ++k) {
// (i1,j1) (i2,j2) 是两条路线到达的位置
for (int i1 = 1; i1 <= n; ++i1) {
for (int i2 = 1; i2 <= n; ++i2) {
int j1 = k - i1, j2 = k - i2;
if (j1 >= 1 && j1 <= n && j2 >= 1 && j2 <= n) {
int t = m[i1][j1];
if (i1 != i2) t += m[i2][j2];
// 四种路线选择
dp[k][i1][i2] = Math.max(dp[k][i1][i2], dp[k - 1][i1 - 1][i2 - 1] + t);
dp[k][i1][i2] = Math.max(dp[k][i1][i2], dp[k - 1][i1 - 1][i2] + t);
dp[k][i1][i2] = Math.max(dp[k][i1][i2], dp[k - 1][i1][i2 - 1] + t);
dp[k][i1][i2] = Math.max(dp[k][i1][i2], dp[k - 1][i1][i2] + t);
}
}
}
}
System.out.println(dp[n + n][n][n]);
}
}
275. 传纸条(转换成 两条路径同时走)
https://www.acwing.com/problem/content/277/
这道题和上一道题其实是一样的。
import java.io.BufferedInputStream;
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sin = new Scanner(new BufferedInputStream(System.in));
int m = sin.nextInt(), n = sin.nextInt();
int[][] a = new int[m + 1][n + 1];
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
a[i][j] = sin.nextInt();
}
}
int[][][] dp = new int[m + n + 1][m + 1][m + 1];
for (int k = 2; k <= m + n; ++k) {
for (int i1 = 1; i1 <= m; ++i1) {
for (int i2 = 1; i2 <= m; ++i2) {
int j1 = k - i1, j2 = k - i2;
if (j1 >= 1 && j1 <= n && j2 >= 1 && j2 <= n) {
int t = a[i1][j1];
if (i1 != i2) t += a[i2][j2];
dp[k][i1][i2] = Math.max(dp[k][i1][i2], dp[k - 1][i1 - 1][i2] + t);
dp[k][i1][i2] = Math.max(dp[k][i1][i2], dp[k - 1][i1 - 1][i2 - 1] + t);
dp[k][i1][i2] = Math.max(dp[k][i1][i2], dp[k - 1][i1][i2 - 1] + t);
dp[k][i1][i2] = Math.max(dp[k][i1][i2], dp[k - 1][i1][i2] + t);
}
}
}
}
System.out.println(dp[m + n][m][m]);
}
}