POJ 3592 Instantaneous Transference(Tarjan缩点 + 记忆化搜索)

题意:

一辆坦克从 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;
}
posted @ 2013-05-12 13:56  kedebug  阅读(596)  评论(0编辑  收藏  举报