题目大意:给出一张地图,有数字的点代表上面有数字个矿物,*代表这个点可以传送到另一个点上,#代表不能走。从一个点只能到这个点的下方和右方。现在从(0,0)开始,问最多可以收集多少矿物。
思路:这个题肯定是建图,然后最长路,关键是有了传送,就有可能形成正权环,然后在SPFA的过程中就会死循环。一个环上的所有权值只能得到一次,所以就用一次Tarjan求出所有的环,把权值累计一下,变成一个点,然后重新建图。这样得到的图就是拓扑图了,没有环,可以无忧无虑的SPFA了。
PS:这个题我用拓扑排序怎么也切不了,求大神告知这是为什么!!!
CODE:
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 20010
using namespace std;
int cases;
int m,n;
char src[50][50];
int transmit[MAX],points;
int head[MAX],total;
int next[MAX],aim[MAX];
int dfn[MAX],low[MAX],_clock;
int stack[MAX],top;
bool in_stack[MAX];
int changed[MAX],scc,val[MAX];
int a[MAX];
int _head[MAX],_total;
int _next[MAX],_aim[MAX];
int f[MAX];
bool v[MAX];
inline void Initialize()
{
total = _total = _clock = top = points = scc = 0;
memset(head,0,sizeof(head));
memset(_head,0,sizeof(_head));
memset(dfn,0,sizeof(dfn));
memset(in_stack,false,sizeof(in_stack));
memset(val,0,sizeof(val));
}
inline void Add(int x,int y)
{
next[++total] = head[x];
aim[total] = y;
head[x] = total;
}
inline void _Add(int x,int y)
{
_next[++_total] = _head[x];
_aim[_total] = y;
_head[x] = _total;
}
void Tarjan(int x)
{
dfn[x] = low[x] = ++_clock;
in_stack[x] = true;
stack[++top] = x;
for(int i = head[x]; i; i = next[i]) {
if(!dfn[aim[i]])
Tarjan(aim[i]),low[x] = min(low[x],low[aim[i]]);
else if(in_stack[aim[i]])
low[x] = min(low[x],dfn[aim[i]]);
}
if(dfn[x] == low[x]) {
scc++;
int temp;
do {
temp = stack[top--];
changed[temp] = scc;
in_stack[temp] = false;
val[scc] += a[temp];
}while(temp != x);
}
}
inline void SPFA()
{
static queue<int> q;
while(!q.empty()) q.pop();
memset(f,0,sizeof(f));
memset(v,false,sizeof(v));
f[changed[0]] = val[changed[0]];
q.push(changed[0]);
while(!q.empty()) {
int x = q.front(); q.pop();
v[x] = false;
for(int i = _head[x]; i; i = _next[i])
if(f[_aim[i]] < f[x] + val[_aim[i]]) {
f[_aim[i]] = f[x] + val[_aim[i]];
if(!v[_aim[i]]) {
v[_aim[i]] = true;
q.push(_aim[i]);
}
}
}
}
int main()
{
for(cin >> cases; cases; --cases) {
scanf("%d%d",&n,&m);
Initialize();
for(int i = 0; i < n; ++i)
scanf("%s",src[i]);
for(int i = 0; i < n; ++i)
for(int x,y,j = 0;j < m; ++j)
if(src[i][j] == '*') {
scanf("%d%d",&x,&y);
a[i * m + j] = 0;
if(src[x][y] != '#')
Add(i * m + j,x * m + y);
if(i + 1 < n && src[i + 1][j] != '#')
Add(i * m + j,(i + 1) * m + j);
if(j + 1 < m && src[i][j + 1] != '#')
Add(i * m + j,i * m + j + 1);
}
else if(src[i][j] != '#') {
a[i * m + j] = src[i][j] - '0';
if(i + 1 < n && src[i + 1][j] != '#')
Add(i * m + j,(i + 1) * m + j);
if(j + 1 < m && src[i][j + 1] != '#')
Add(i * m + j,i * m + j + 1);
}
for(int i = 0; i < m * n; ++i)
if(!dfn[i]) Tarjan(i);
for(int x = 0; x < m * n; ++x)
for(int i = head[x]; i; i = next[i])
if(changed[x] != changed[aim[i]])
_Add(changed[x],changed[aim[i]]);
SPFA();
int *ans = max_element(f + 1,f + scc + 1);
printf("%d\n",*ans);
}
return 0;
}