题意:
一辆坦克从 N*M 矩阵的左上角出发,每次往右或往 下走一格,每格可以是 '#' (表示不可以走), '*' 表示传送门,或者是数字,
表示在该格可以获得的值(只能取一次),传送门可以传送到指定位置,你可以选择被传送或者走相邻的格,问坦克可以获得的值的和最大为多少。
思路:
1. 本题是由几个比较基本的问题组合而成的,关键的问题是在于建图,感谢前面几个网络流题目所培养的建图思想。
2. 因为坦克只能往右下角的方向走,所以在满足条件的情况下,可以由 (i, j) 向 (i+1, j) or (i, j+1) 引弧。因为传送门的存在,还可以再引弧。
3. 建图之后,由于传送门的存在可能会存在环,tarjan 算法化环为点,然后在单向图中记忆化搜索便可得出输出结果。
#include <iostream>
#include <stack>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = 1610;
vector<int> G[MAXN];
vector<int> G1[MAXN];
stack<int> S;
int dfn[MAXN], low[MAXN], sccno[MAXN], sccnum, cflag;
int row, col, F[MAXN], ore[MAXN], w[MAXN];
char map[50][50];
void tarjan(int u) {
dfn[u] = low[u] = ++cflag;
S.push(u);
for (int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
} else if (!sccno[v]) {
low[u] = min(low[u], dfn[v]);
}
}
if (low[u] == dfn[u]) {
sccnum += 1;
int v = -1;
while (v != u) {
v = S.top();
S.pop();
sccno[v] = sccnum;
w[sccnum] += ore[v];
}
}
}
void findscc(int n) {
for (int i = 0; i <= n; i++)
dfn[i] = low[i] = sccno[i] = 0;
sccnum = cflag = 0;
for (int i = 1; i <= n; i++)
if (!dfn[i]) tarjan(i);
}
int dp(int u) {
if (F[u]) return F[u];
F[u] = w[u];
int x = 0;
for (int i = 0; i < G1[u].size(); i++) {
int v = G1[u][i];
x = max(x, dp(v));
}
if (x) F[u] += x;
return F[u];
}
inline int ID(int i, int j) { return i * col + j + 1; }
int main() {
int cases;
scanf("%d", &cases);
while (cases--) {
scanf("%d%d", &row, &col);
int n = ID(row - 1, col - 1);
for (int i = 1; i <= n; i++)
G[i].clear(), G1[i].clear();
for (int i = 0; i < row; i++)
scanf("%s", map[i]);
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (map[i][j] == '#') continue;
if (i + 1 < row && map[i+1][j] != '#')
G[ID(i, j)].push_back(ID(i+1, j));
if (j + 1 < col && map[i][j+1] != '#')
G[ID(i, j)].push_back(ID(i, j+1));
ore[ID(i, j)] = map[i][j] - '0';
if (map[i][j] == '*') {
int x, y;
scanf("%d%d", &x, &y);
if (map[x][y] != '#')
G[ID(i, j)].push_back(ID(x, y));
ore[ID(i, j)] = 0;
}
}
}
for (int i = 0; i <= n; i++)
F[i] = w[i] = 0;
findscc(n);
for (int u = 1; u <= n; u++) {
for (int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
if (sccno[u] != sccno[v])
G1[sccno[u]].push_back(sccno[v]);
}
}
printf("%d\n", dp(sccno[ID(0, 0)]));
}
return 0;
}