【题意】:给出一个n*m的矩阵,每个格子可能放有0~9,#,*。0~9代表货物的数量,#代表阻碍,不能够走,*代表传送点,可以瞬间传送到特定的位置,对于传送点的传送功能可以无限使用,也可以不用。初始时你开着一辆小卡车在最左上角,每一次你都可以向右走一格或者向下走一格,如果该格子上放有货物的话你可以把他拿走,但只能拿一次。问最后你能够拿到最多的货物是多少?

【题解】:这是一道方格取数的变形题目,加入了阻碍与传送点。如果按平时方格取数那样做的话,由于传送点的存在,图中可能有环从而导死循环。于是我们想到了缩点,缩点后的新图显然是一个有向无环图,对于同一连通分量上的货物我们都可以拿走,这里留给大家自己思考为什么了,之后该怎么做就怎么做了。实际操作时,我们可以先按平时的方格取数构图,对于传送点i,假设si是传送点的本身位置,ti是传送点的目标位置,连边si->ti,然后进行缩点构新图,缩点之后的做法有很多,DP、搜索、求最长路都可以,我这里是求最长路。

【代码】:
  1 #include "iostream"
  2 #include "cstdio"
  3 #include "cstring"
  4 #include "vector"
  5 #include "queue"
  6 using namespace std;
  7 #define maxn 1605
  8 const int inf = 1 << 30;
  9 struct Edge {
 10     int v, w;
 11 };
 12 int n, m;
 13 char maz[50][50];
 14 int dfn[maxn], low[maxn], Dindex, scc, belong[maxn], dist[maxn], score[maxn], w[maxn];;
 15 int magic[maxn], cnt;
 16 int s[maxn], top;
 17 bool instack[maxn], visit[maxn];
 18 vector<int> vec[maxn];
 19 vector<Edge> dag[maxn];
 20 
 21 void dfs(int u) {
 22     int v;
 23     dfn[u] = low[u] = ++Dindex;
 24     s[++top] = u;
 25     instack[u] = true;
 26     int size = vec[u].size();
 27     for(int i = 0; i < size; i++) {
 28         v = vec[u][i];
 29         if(!dfn[v]) {
 30             dfs(v);
 31             low[u] = min(low[v], low[u]);
 32         } else if(instack[v]) low[u] = min(low[u], dfn[v]);
 33     }
 34     if(dfn[u] == low[u]) {
 35         w[++scc] = 0;
 36         do {
 37             v = s[top--];
 38             instack[v] = false;
 39             belong[v] = scc;   
 40             w[scc] += score[v];
 41         }while(v != u);
 42     }
 43 }
 44 
 45 void tarjan() {
 46     top = scc = Dindex = 0;
 47     memset(instack, falsesizeof(instack));
 48     memset(dfn, 0sizeof(dfn));
 49     for(int i = 0; i < n * m; i++)
 50         if(!dfn[i]) dfs(i);
 51     for(int u = 0; u < n * m; u++) {
 52         int size = vec[u].size();
 53         for(int i = 0; i < size; i++) {
 54             int v = vec[u][i];
 55             if(belong[u] != belong[v]) {
 56                 Edge e = {belong[v], w[belong[v]]};
 57                 dag[belong[u]].push_back(e);
 58             }
 59         }
 60     }
 61     Edge e = {belong[0], w[belong[0]]};
 62     dag[0].push_back(e);
 63 }
 64 
 65 int spfa(int s) {
 66     fill(&dist[0], &dist[maxn], -inf);
 67     memset(visit, falsesizeof(visit));
 68     dist[s] = 0, visit[s] = true;
 69     queue<int> que;
 70     que.push(s);
 71     while(!que.empty()) {
 72         int u = que.front();
 73         que.pop();
 74         visit[u] = false;
 75         int size = dag[u].size();
 76         for(int i = 0; i < size; i++) {
 77             int v = dag[u][i].v, w = dag[u][i].w;
 78             if(dist[v] < dist[u] + w) {
 79                 dist[v] = dist[u] + w;
 80                 if(!visit[v]) {
 81                     que.push(v);
 82                     visit[v] = false;
 83                 }
 84             }
 85         }
 86     }
 87     int ans = 0;
 88     for(int i = 0; i <= scc; i++)
 89         ans = max(ans, dist[i]);
 90     return ans;
 91 }
 92 
 93 int main() {
 94     int T;
 95     scanf("%d"&T);
 96     while(T--) {
 97         scanf("%d%d"&n, &m);
 98         getchar();
 99         cnt = 0;
100         for(int i = 0; i < maxn; i++) vec[i].clear(), dag[i].clear();
101         for(int i = 0; i < n; i++) scanf("%s", maz[i]);
102         for(int i = 0; i < n; i++) {
103             for(int j = 0; j < m; j++) {
104                 int u = i * m + j;
105                 if(maz[i][j] == '#') score[u] = -1;
106                 else {
107                     if(maz[i][j] == '*') {
108                         magic[cnt++= u;
109                         score[u] = 0;
110                     }
111                     else score[u] = maz[i][j] - '0';
112                     if(j + 1 < m && maz[i][j+1!= '#') vec[u].push_back(u + 1);
113                     if(i + 1 < n && maz[i+1][j] != '#') vec[u].push_back(u + m);
114                 }
115             }
116         }
117         for(int i = 0; i < cnt; i++) {
118             int ii, jj, v;
119             scanf("%d%d"&ii, &jj);
120             v = ii * m + jj;
121             if(score[v] == -1continue;
122             vec[magic[i]].push_back(v);
123         }
124         tarjan();    //缩点构新图 - dag
125         cout << spfa(0<< endl;
126     }
127     return 0;
128 }