方格取数

方格取数

设有 N×N 的方格图,我们在其中的某些方格中填入正整数,而其它的方格中则放入数字 0。如下图所示:

某人从图中的左上角 A 出发,可以向下行走,也可以向右行走,直到到达右下角的 B 点。

在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字 0)。

此人从 A 点到 B 点共走了两次,试找出两条这样的路径,使得取得的数字和为最大。

输入格式

第一行为一个整数 N,表示 N×N 的方格图。

接下来的每行有三个整数,第一个为行号数,第二个为列号数,第三个为在该行、该列上所放的数。

行和列编号从 1 开始。

一行“0 0 0”表示结束。

输出格式

输出一个整数,表示两条路径上取得的最大的和。

数据范围

N10

输入样例:

复制代码
8
2 3 13
2 6 6
3 5 7
4 4 14
5 2 21
5 6 4
6 3 15
7 2 14
0 0 0
复制代码

输出样例:

67

 

解题思路

  一开始想到一个贪心的思路,就是两遍dp。第一遍dp得到从(1,1)(n,n)的最大值,然后把走过的格子的值置成0,然后继续进行一次dp,得到从(1,1)(n,n)的最大值,两次得到的最大值的和就是答案。但这种贪心做法是错的,因为第一次dp的结果会影响第二次dp的结果,举个反例,一开始格子的值如下:

  因此这题应该用dp。由于是两条路径,因此状态应该有4维,即f(i,j,u,v)表示从(1,1)走到(i,j)的第1条路径以及从(1,1)走到(u,v)的第2条路径的集合。根据分别到达(i,j)(u,v)的方向进行状态划分,共22种状态,状态转移方程就是f(i,j,u,v)=max{f(i1,j,u1,v), f(i1,j,u,v1), f(i,j1,u1,v), f(i,j1,u,v1)}+w

  其中w={wij+wuv(iu | jv)wij(i=u & j=v)

  如果有i=u并且j=v,那么应该算一个格子值,即状态转移方程应该只加上wij而不再加同一个格子的值wuv

  AC代码如下:

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 20;
 5 
 6 int g[N][N], f[N][N][N][N];
 7 
 8 int main() {
 9     int n, a, b, c;
10     scanf("%d", &n);
11     while (scanf("%d %d %d", &a, &b, &c), a || b || c) {
12         g[a][b] = c;
13     }
14     
15     for (int i = 1; i <= n; i++) {
16         for (int j = 1; j <= n; j++) {
17             for (int u = 1; u <= n; u++) {
18                 for (int v = 1; v <= n; v++) {
19                     int w = g[i][j];
20                     if (i != u || j != v) w += g[u][v]; // 不是同一个格子
21                     f[i][j][u][v] = max({f[i - 1][j][u - 1][v], f[i - 1][j][u][v - 1], f[i][j - 1][u - 1][v], f[i][j - 1][u][v - 1]}) + w;
22                 }
23             }
24         }
25     }
26     
27     printf("%d", f[n][n][n][n]);
28     
29     return 0;
30 }
复制代码

  可以发现,当i+j=u+v的时候,两条路径的格子才可能重合。因此我们可以规定两条路径是同时出发的,这样就可以优化掉一维,得到f(k,i,j)。其中k为两条路径的横纵坐标的和(因为两条路径是同时出发的,因此两条路径的横纵坐标和相同),i为第一条路径的横坐标,j为第二条路径的横坐标。ki就得到第一条路径的纵坐标,kj就得到第二条路径的纵坐标。

  如果ij,说明不是在同一个格子。状态转移方程为f(k,i,j)=max{f(k1,i1,j1), f(k1,i1,j), f(k1,i,j1), f(k1,i,j)}+w

  其中w={wij+wij(ij)wij(i=j)

  AC代码如下:

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 30;
 5 
 6 int g[N][N], f[N << 1][N][N];
 7 
 8 int main() {
 9     int n, a, b, c;
10     scanf("%d", &n);
11     while (scanf("%d %d %d", &a, &b, &c), a || b || c) {
12         g[a][b] = c;
13     }
14     
15     for (int k = 2; k <= n + n; k++) {
16         for (int i = 1; i <= n; i++) {
17             for (int j = 1; j <= n; j++) {
18                 if (k - i < 0 || k - j < 0) continue;   // 判断纵坐标是否越界
19                 int w = g[i][k - i];
20                 if (i != j) w += g[j][k - j];   // 两个格子不同
21                 f[k][i][j] = max({f[k - 1][i - 1][j - 1], f[k - 1][i - 1][j], f[k - 1][i][j - 1], f[k - 1][i][j]}) + w;
22             }
23         }
24     }
25     
26     printf("%d", f[n + n][n][n]);
27     
28     return 0;
29 }
复制代码

 

参考资料

  AcWing 1027. 方格取数:https://www.acwing.com/video/354/

posted @   onlyblues  阅读(864)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示