题解:洛谷 P9014 Following Directions
【题目来源】
洛谷:P9014 [USACO23JAN] Following Directions S - 洛谷
【题目描述】
Farmer John 有一个正方形的草地,草地被划分为了 \((N+1)\times (N+1)(1\le N\le 1500)\) 的格子。设 \((i,j)\) 为从上到下、从左到右第 \(i\) 行,第 \(j\) 列的格子。每个满足 \(1\le i,j\le n\) 的格子 \((i,j)\) 之中都住着一头牛,而且每个这样的格子上都有一个路标指向右或下。除此之外,所有满足 \(i=N+1\) 或 \(j=N+1\) 的格子,除了 \((N+1,N+1)\) 都会有一个饲料桶。牛在每个饲料桶进食需要的价格不同;位置 \((i,j)\) 上的桶喂饱一只牛需要价格 \(c_{i,j}(1\le c_{i,j}\le 500)\)。
每天晚饭时间,Farmer John 摇响晚餐铃时,所有牛都沿着路标的指向前进,直到它们遇到了饲料桶,之后它们会在它们自己遇到的饲料桶那里进食。第二天,所有牛又会回到自己原来的位置。
为了维持预算,Farmer John 想要知道每天喂食需要的价钱。然而,每天晚饭之前,总会有一头牛 \((i,j)\) 翻转它那里的路标(原来向下则变成向右,反之亦然)。被翻转的路标指向将在后面的日子里保持不变,除非它又被进行了翻转。
给出每天被翻转的路标的坐标,请输出每天喂食需要的价格(总共有 Q 天,\(1\le Q\le 1500\))。
【输入】
第一行为 \(N(1\le N\le 1500)\)
接下来的 \(N\)\(+1\) 行从上到下输入初始的路标朝向和每个饲料桶的价格 \(c_{i,j}\)。前 \(N\) 行每行包含一个长度为 \(N\) 的字符串,其中每个字符只能是 R 或 D(R 表示向右,D 表示向下),之后是一个数,表示价格 \(c_{i,N+1}\),第 \((N+1)\) 行包含 \(N\) 个数,依次表示价格 \(c_{N+1,j}\)。
接下来的一行为 \(Q(1\le Q\le 1500)\)。
之后的 \(Q\) 行,每行有两个整数 \(i\) 和 \(j(1\le i,j\le N)\),表示每天被翻转的路标的坐标。
【输出】
共 \(Q+1\) 行:第一行是初始的总价格,之后 \(Q\) 行依次是每次被翻转后的总价格。
【输入样例】
2
RR 1
DD 10
100 500
4
1 1
1 1
1 1
2 1
【输出样例】
602
701
602
701
1501
【核心思想】
-
问题分析:给定一个 \((N+1) \times (N+1)\) 的网格,前 \(N \times N\) 每个格子有路标
R(右)或D(下),边界有饲料桶及价格。每头牛沿路标走到边界进食。每天翻转一个路标,求每天的总喂食价格。本质上是递推计数 + 增量更新问题:用sum[r][c]表示经过格子 \((r,c)\) 的牛的数量,递推计算后通过增量更新处理翻转。 -
算法选择:
- 递推求路径数:
sum[r][c]初始为 \(1\)(每格一头牛),按路标方向累加传播到边界 - 增量更新:翻转路标时,先沿原方向减去该格子的贡献,翻转后再沿新方向加回贡献
- 递推求路径数:
-
关键步骤:
- 读取数据:读入 \(N\),\(N \times N\) 路标和行末价格
costR,以及底部价格costD - 初始化:
sum[r][c] = 1(\(1 \leq r, c \leq N\)) - 递推传播(\(r = 1\) 到 \(N\),\(c = 1\) 到 \(N\)):
- 若
dir[r][c] == 'R':sum[r][c+1] += sum[r][c] - 若
dir[r][c] == 'D':sum[r+1][c] += sum[r][c]
- 若
- 计算总价格:
ans = sum[r][N+1] * costR[r] + sum[N+1][c] * costD[c] - 处理 \(Q\) 次翻转:
- 保存
tmpR = r, tmpC = c, tmpSum = sum[r][c] - 减去原贡献:沿原路标方向走,每步
sum减去tmpSum - 翻转路标:
R\(\leftrightarrow\)D - 加回新贡献:沿新路标方向走,每步
sum加上tmpSum - 输出新的总价格
- 保存
- 读取数据:读入 \(N\),\(N \times N\) 路标和行末价格
-
时间/空间复杂度:
- 时间复杂度:\(O(N^2 + Q \cdot N)\),初始化 \(O(N^2)\),每次翻转更新 \(O(N)\)
- 空间复杂度:\(O(N^2)\),
sum和dir数组
-
递推计数 + 增量更新的核心思想:
- 路径数的叠加原理:每格子的牛按路标流向下一格,
sum数组记录流经每个格子的牛数,边界格子的sum即为到达该饲料桶的牛数 - 增量更新的正确性:翻转一个路标只影响从该格子出发的所有下游路径,减去原路径贡献再加回新路径贡献,其他格子不受影响
- 单向传播:路标只有
R和D两个方向,保证无环,递推顺序从左到右、从上到下即可 - 价格计算:边界格子的
sum乘以对应饲料桶价格即为该桶总花费 - 适用于网格路径、动态更新、流量统计类问题
- 路径数的叠加原理:每格子的牛按路标流向下一格,
【解题思路】

【算法标签】
普及 #递推
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int maxN = 1505;
char dir[maxN][maxN];
int n, sum[maxN][maxN], costR[maxN], costD[maxN];
int calc()
{
int ans = 0;
for (int r=1; r<=n; r++) { // 遍历所有行
ans += sum[r][n+1] * costR[r]; // 计算每行的价格,并进行累加
}
for (int c=1; c<=n; c++) { // 遍历所有列
ans += sum[n+1][c] * costD[c]; // 计算每列的价格,并累加
}
return ans; // 返回总价格
}
int main()
{
cin >> n; // 输入n
for (int r=1; r<=n; r++) { // 输入所有字符
for (int c=1; c<=n; c++) {
cin >> dir[r][c];
}
cin >> costR[r]; // 每行最后一个数字
}
for (int c=1; c<=n; c++) { /// 输入每列的价格
cin >> costD[c];
}
for (int r=1; r<=n; r++) { // 每个位置的sum[r][c]初始为1,用于后面递推
for (int c=1; c<=n; c++) {
sum[r][c] = 1;
}
}
for (int r=1; r<=n; r++) { // 进行递推
for (int c=1; c<=n; c++) {
if (dir[r][c]=='R') { // 如果当前字符为'R'
sum[r][c+1] += sum[r][c]; // 右边的sum[r][c]加上当前值
}
if (dir[r][c]=='D') { // 如果当前字符为'D'
sum[r+1][c] += sum[r][c]; // 下面的sum[r][c]加上当前值
}
}
}
cout << calc() << endl; // 输出未翻转前的总价格
int q;
cin >> q; // 输入q
while (q--) { // 遍历q次询问
int r, c;
cin >> r >> c; // 输入被翻转的坐标
int tmpR = r, tmpC = c, tmpSum = sum[r][c]; // 临时变量用来保存r、c和tmpSum,因为r和c要变化
while (r<=n && c<=n) { // 先从r->n和c->n,每个位置减掉tmpSum
if (dir[r][c] == 'R') { // 如果当前字符为'R'
sum[r][++c] -= tmpSum; // 则右边的sum[r][c]减去tmpSum
} else { // 如果当前字符为'D'
sum[++r][c] -= tmpSum; // 则下边的sum[r][c]减去tmpSum
}
}
r = tmpR, c = tmpC; // 还原r和c
if (dir[r][c]=='R') { // 设定翻转后的路标
dir[r][c] = 'D';
} else {
dir[r][c] = 'R';
}
while (r<=n && c<=n) { // 再次遍历r->c和c->n,每个位置再加回来tmpSum
if (dir[r][c] == 'R') { // 如果当前字符为'R'
sum[r][++c] += tmpSum; // 则右边的sum[r][c]减去tmpSum
} else { // 如果当前字符为'D'
sum[++r][c] += tmpSum; // 则下边的sum[r][c]减去tmpSum
}
}
cout << calc() << endl; // 输出每轮调整后的总和
}
return 0;
}
【运行结果】
2
RR 1
DD 10
100 500
602
4
1 1
701
1 1
602
1 1
701
2 1
1501
浙公网安备 33010602011771号