题解:AT_abc210_d [ABC210D] National Railway
涉及知识点:动态规划
解题思路
因为 \(W\) 和 \(H\) 的范围都很大,直接枚举两个车站的位置肯定会时间超限的,所以考虑动态规划。
定义 \(dp_{i, j}\) 表示所有横坐标小于等于 \(i\) 且纵坐标小于等于 \(j\) 的点的最小的 \(a_{i', j'} + c\times (i'+j')\) 的值。
非常容易可以得到动态转移方程:
\[dp_{i, j} = \min(dp_{i, j}, a_{i, j} + c\times (i + j))
\]
每个 \(dp_{i, j}\) 的初始值为:
\[dp_{i, j} = \min(dp_{i - 1, j}, dp_{i, j - 1}, dp_{i - 1, j - 1})
\]
答案为:
\[res = \min(res, dp_{i, j} + a_{i, j} + c\times (i + j))
\]
我们发现如果只按照上面的方法做一次动态规划,相当于从矩阵的左上角向右下角转移了一次,并没有考虑到全部情况,还需要从矩阵的右上角向左下角转移一次,相当于把矩阵的每行调转顺序,再按上面的方法转移一次。
最后输出 \(res\) 即可。
小提醒:由于当前点 \((i, j)\) 的值可能会比没有更新的 \(dp_{i,j}\) 的值更优秀,所以需要先初始化 \(dp_{i, j}\),然后更新 \(res\),最后计算 \(dp_{i, j}\)。
代码
丑陋的赛时代码。
这里我就只放了核心部分,剩余的部分请读者自己完成。
res = inf;
memset(dp, 0x3f, sizeof dp);
rep(i, 1, n) rep(j, 1, m) {
dp[i][j] = min({dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]});
if (i > 1 || j > 1) res = min(res, dp[i][j] + a[i][j] + c * (i + j));
if (i > 1 || j > 1) dp[i][j] = min(dp[i][j], a[i][j] - c * (i + j));
else dp[i][j] = a[i][j] - c * (i + j);
}
memset(dp, 0x3f, sizeof dp);
rep(i, 1, n) reverse(a[i] + 1, a[i] + m + 1);
rep(i, 1, n) rep(j, 1, m) {
dp[i][j] = min({dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]});
if (i > 1 || j > 1) res = min(res, dp[i][j] + a[i][j] + c * (i + j));
if (i > 1 || j > 1) dp[i][j] = min(dp[i][j], a[i][j] - c * (i + j));
else dp[i][j] = a[i][j] - c * (i + j);
}
cout << res << endl;