题解:学而思编程 平衡的路径

【题目来源】

学而思编程:平衡的路径

【题目描述】

有一个 \(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

【核心思想】

  1. 问题分析:给定 \(H \times W\) 网格,每个格子有两个数 \(A_{ij}\)\(B_{ij}\)。从 \((1,1)\) 走到 \((H,W)\),每次只能向右或向下。对每个经过的格子可以选择将 \(A\) 涂红、\(B\) 涂蓝,或反之。求红数和 \(R\) 与蓝数和 \(B\) 之差的绝对值 \(|R-B|\) 的最小值。这是一个三维DP问题,关键在于用布尔数组记录可达的差值状态。

  2. 算法选择

    • 三维DPdp[i][j][k] 表示到达 \((i,j)\) 时,差值为 \(k\) 是否可达
    • 偏移量处理:使用偏移量 \(P\) 处理负数差值(\(k\) 实际表示差值 \(k-P\)
    • 路径选择:每个格子可以选择 \(+t\)\(-t\)\(t = A_{ij} - B_{ij}\)
  3. 关键步骤

    • 初始化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] 为真
  4. 时间/空间复杂度

    • 时间复杂度:\(O(H \times W \times 2P)\),其中 \(P\) 是偏移量(约为最大可能差值)
    • 空间复杂度:\(O(H \times W \times 2P)\)
  5. 三维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
posted @ 2026-06-14 10:46  团爸讲算法  阅读(7)  评论(0)    收藏  举报