斜向遍历——方格取数
AcWing1027方格取数的理解
设有 N×N 的方格图,我们在其中的某些方格中填入正整数,而其它的方格中则放入数字0。如下图所示:
2.gif
某人从图中的左上角 A 出发,可以向下行走,也可以向右行走,直到到达右下角的 B 点。
在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字0)。
此人从 A 点到 B 点共走了两次,试找出两条这样的路径,使得取得的数字和为最大。
输入格式
第一行为一个整数N,表示 N×N 的方格图。
接下来的每行有三个整数,第一个为行号数,第二个为列号数,第三个为在该行、该列上所放的数。
行和列编号从 1
开始。
一行“0 0 0”表示结束。
输出格式
输出一个整数,表示两条路径上取得的最大的和。
数据范围
N≤10
输入样例:
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
(这是一道难度定为简单的题,但是对于我这菜鸟还是太超前了,写了篇题解,头一次写这么长,但是很水,有错误的地方还请大佬指正, 这是我个人对本题解中所附代码的理解,如果有好的方法或者理解方式,敬请分享)
方格取数的读入没什么好解释的,相较于其他的,最核心的部分应该也就属这段代码了!
`
for (int k = 2; k <= n + n; k )
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 = w[i1][j1];
if (i1 != i2) t += w[i2][j2];
int &x = f[k][i1][i2];
x = max(x, f[k - 1][i1 - 1][i2 - 1] + t);
x = max(x, f[k - 1][i1 - 1][i2] + t);
x = max(x, f[k - 1][i1][i2 - 1] + t);
x = max(x, f[k - 1][i1][i2] + t);
}
}
`
- 方格图的斜向遍历, 插一张图哈
先看这张图, 图中每个方格中的数字,是代表着一个k 值, 图中的纵坐标和横坐标, 代表的分别是i 和 y 值,图中的k值是互不影响的,是自左上向右下顺次填的连续递增的k值, 为什么呢,当一步都还没有向外走时, k值是2, k值是i1 j1 或者 i2 j2 的和, 由于i1 y1 和 i2 y2是分别平行的,所以就举例一个了, ok, 回归上一个为什么,现在可以表示k = i + j, 也就是说, 在各种情况枚举的合法的情况下, i 和 j是密切联系的。 - 举个具体的例子, 当k 取6 时, i 的合法取值是2, 3, 4, 然后对应的j 分别是4, 3, 2,在图中正好是一个斜线,如果现在i 取1, 此时j 的值是5, 明显大于4, 所以不合法,i 取 5, 亦不合法。在举个例子, 如果现在k 取3, 那么i 的合法取值是1, 2, 分别对应的j 是2, 1, 连起来又是一条斜线
- 我感觉现在图的斜向遍历模拟我感觉莫有问题了, 现在来看一下数值改变的模拟
- 还是举例为主哈,现在我们取k 为6, 上面已经说过了,i 此时的合法取值是2, 3, 4,j 的合法取值分别是 4, 3, 2,根据上面的代码哈
f[6][3][3] = max(f[5][3][3], f[5][3][2], f[5][2][3], f[5][2][2]) + t; (t = w[i1][j1]); (t = w[3][3]);
维度解释,f[6][3][3] 中6 是代表着走了(6 - 2)步,第一个3代表着 i1 取到了3,j1 取到了(6 - 3), i2 取到了3, j2 取到了(6 - 3)。(第(6 - 2)步中的值, 取决于(5 - 2)步中的值)
f[6][2][3] = max(f[5][2][3], f[5][1][3], f[5][2][2], f[5][1][2]) + t; (t = w[i1][j1] + w[i2][j2]); (w = w[2][4] + w[3][3])
我感觉举上面两个例子应该就ok了,其实这就覆盖了两大实际情况,一种是上一步中从同一个格子中过来,另一种就是上一步中从不同格子来, 如果从相同格子来,t 值只包含一个一个格子中的值,如果从不同格子来,t 值包含两个格子中的值
但是当t 值中只包含一个值时, 好像也就左上角顶点和右下角的顶点可以保留这个t值, 其他的都会在中间滞留或者说就是那个值报废掉了
比如说左上角这个
f[2][1][1] = max(f[1][1][1], f[1][0][1], f[1][1][0], f[1][0][0]) + t; (t = w[1][1]);
数值向下一步传递过程中,会出现“违法”坐标的出现,但是f 数组定义的是全局变量,所以默认值是0;
f[3][1][1] = max(f[2][1][1], f[2][1][0], f[2][0][1], f[2][0][0]) + t; (t = w[1][2]);
f[3][2][1] = max(f[2][2][1], f[2][1][1], f[2][2][0], f[2][1][0]) + t; (t = w[2][1] + w[1][2]);
f[3][1][2] = max(f[2][1][2], f[2][0][2], f[2][1][1], f[2][0][1]) + t; (t = w[1][2] + w[2][1]);
f[3][2][2] = max(f[2][2][2], f[2][1][2], f[2][2][1], f[2][1][1]) + t; (t = w[2][2]);
其实上面有些部分感觉重复,但好像都是必要的
如果说有两个值, 一个是a, 一个是b,而且都是非负数,那必须有a + b >= a 或者 a + b >= b, 所以只包含一个值的只有连个端点可以保留
展示一下解决问题的代码吧
`
#include [HTML_REMOVED]
include [HTML_REMOVED]
using namespace std;
const int N = 15;
int n;
int w[N][N];
int f[N * 2][N][N];
int main(){
scanf(“%d”, &n);
int a, b, c;
while (cin >> a >> b >> c, a || b || c) w[a][b] = c;
for (int k = 2; k <= n + n; k ++)
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 = w[i1][j1];
if (i1 != i2) t += w[i2][j2];
int &x = f[k][i1][i2];
x = max(x, f[k - 1][i1 - 1][i2 - 1] + t);
x = max(x, f[k - 1][i1 - 1][i2] + t);
x = max(x, f[k - 1][i1][i2 - 1] + t);
x = max(x, f[k - 1][i1][i2] + t);
}
}
printf("%d\n", f[n + n][n][n]);
return 0;
}
`