题解:学而思编程 平衡的路径
【题目来源】
学而思编程:平衡的路径
【题目描述】
有一个 \(H\) 行 \(W\) 列的棋盘第 \(i\) 行第 \(j\) 列的棋盘格记为 \((i,j)\)。每个格子中都写了两个数,\((i,j)\) 中写有整数 \(A_{ij}\) 和 \(B_{ij}\)。
小明首先对每个格子里的两个数染色:一个涂成红色,另一个涂成蓝色。
染色完成后,小明从左上角 \((1,1)\) 出发,每次可以向右或向下走一格,直到到达右下角 \((H,W)\)。途中经过的所有格子(包括起点和终点)中红色数字之和记为 \(R\),蓝色数字之和记为 \(B\)。
通过适当的染色以及选取合适的路径,小明想要 \(R\) 和 \(B\) 之差的绝对值尽可能的小。问 \(|R−B|\) 的最小值是多少?
【输入】
输入共 \(2H+1\) 行
第 \(1\) 行,两个正整数 \(H,W\)
第 \(2\) 到 \(H+1\) 行,每行 \(W\) 个整数,第 \(i+1\) 行第 \(j\) 个数为 \(A_{ij}\)
第 \(H+2\) 到 \(2H+1\) 行,每行 \(W\) 个整数,第 \(i+H+1\) 行第 \(j\) 个数为 \(B_{ij}\)
【输出】
输出 \(|R−B|\) 的最小值
【输入样例】
2 2
1 2
3 4
3 4
2 1
【输出样例】
0
【核心思想】
-
问题分析:给定 \(H \times W\) 网格,每个格子有两个数 \(A_{ij}\) 和 \(B_{ij}\)。从 \((1,1)\) 走到 \((H,W)\),每次只能向右或向下。对每个经过的格子可以选择将 \(A\) 涂红、\(B\) 涂蓝,或反之。求红数和 \(R\) 与蓝数和 \(B\) 之差的绝对值 \(|R-B|\) 的最小值。这是一个三维DP问题,关键在于用布尔数组记录可达的差值状态。
-
算法选择:
- 三维DP:
dp[i][j][k]表示到达 \((i,j)\) 时,差值为 \(k\) 是否可达 - 偏移量处理:使用偏移量 \(P\) 处理负数差值(\(k\) 实际表示差值 \(k-P\))
- 路径选择:每个格子可以选择 \(+t\) 或 \(-t\)(\(t = A_{ij} - B_{ij}\))
- 三维DP:
-
关键步骤:
- 初始化:
dp[0][1][P] = 1,起点差值为0(加上偏移量 \(P\)) - 状态转移:遍历每个格子 \((i,j)\) 和每个可能的差值 \(k\):
- 计算当前格子的差值 \(t = A_{ij} - B_{ij}\)
- 选择 \(+t\)(\(A\) 红 \(B\) 蓝):若 \(k \geq t\),则
dp[i][j][k] |= dp[i-1][j][k-t] || dp[i][j-1][k-t] - 选择 \(-t\)(\(A\) 蓝 \(B\) 红):若 \(k + t \leq 2P\),则
dp[i][j][k] |= dp[i-1][j][k+t] || dp[i][j-1][k+t]
- 统计答案:从0开始枚举 \(i\),找到最小的 \(|差值|\) 使得
dp[H][W][P+i]或dp[H][W][P-i]为真
- 初始化:
-
时间/空间复杂度:
- 时间复杂度:\(O(H \times W \times 2P)\),其中 \(P\) 是偏移量(约为最大可能差值)
- 空间复杂度:\(O(H \times W \times 2P)\)
-
三维DP + 偏移量处理的核心思想:
- 布尔状态设计:用
dp[i][j][k]记录差值是否可达,而非记录具体数值 - 偏移量技巧:将差值范围 \([-P, P]\) 映射到数组下标 \([0, 2P]\),处理负数下标
- 两种选择:每个格子可以选择加 \(t\) 或减 \(t\),对应两种染色方式
- 路径方向:只能向右或向下,状态从上方或左方转移
- 适用于带路径约束和差值优化的问题
- 布尔状态设计:用
【算法标签】
线性DP-二维
【代码详解】
#include<iostream>
#include<algorithm>
using namespace std;
const int P = 12720; // 偏移量,用于处理负数下标
int h, w, a[85][85], b[85][85];
bool dp[85][85][2 * P + 5]; // DP数组,dp[i][j][k]表示到达(i,j)时差值为k是否可达
int main()
{
cin >> h >> w;
for (int i = 1; i <= h; i++)
{
for (int j = 1; j <= w; j++)
{
cin >> a[i][j];
}
}
for (int i = 1; i <= h; i++)
{
for (int j = 1; j <= w; j++)
{
cin >> b[i][j];
}
}
dp[0][1][P] = 1; // 初始化起点,差值为0(加上偏移量P)
for (int i = 1; i <= h; i++)
{
for (int j = 1; j <= w; j++)
{
int t = a[i][j] - b[i][j]; // 当前单元格的差值
for (int k = 0; k <= 2 * P; k++)
{
// 从上方或左方转移,可以选择加或减当前差值
if (k >= t)
{
dp[i][j][k] |= (dp[i - 1][j][k - t] || dp[i][j - 1][k - t]);
}
if (k + t <= 2 * P)
{
dp[i][j][k] |= (dp[i - 1][j][k + t] || dp[i][j - 1][k + t]);
}
}
}
}
// 寻找最小的绝对差值
for (int i = 0; i <= P; i++)
{
if (dp[h][w][P + i] || dp[h][w][P - i]) // 差值可以是正或负
{
cout << i;
return 0;
}
}
return 0;
}
【运行结果】
2 2
1 2
3 4
3 4
2 1
0
浙公网安备 33010602011771号