超时空传送

超时空传送( tarjan\(\star \))

  • 时限:\(1s\) 内存:\(256M\)

Descrption

  • 你一定知道红色警戒这款游戏吧,在这个游戏里有一种神奇的科技被称作超时空传送,当一个物体使用这种神奇的科技时,它会直接被传送到某个规定的位置,不论距离有多远。
  • 现在这里有一处矿区,而你正操控着一个采矿车。你的任务是在这个矿区获取最多的矿石。这个矿区是一个由 \(n\times m\) 个小格子组成的矩形区域,其中一些格子有矿石,而另一些没有。矿石只能被采集一遍,而不会重新产生。
  • 矿车的初始位置为该矩形地区的西北角。它只能向东或向南的临近的格子移动,而不能向北或向西移动。一些格子有传送门而使矿车可以通过超时空传送到达某个特定的格子。
  • 因为你是这个矿车的指挥,所以你要来决定是否使用传送门(可以选择留在原地)。而这种传送门是永远不会消失的,当你到达有这种传送门的格子时,就可以立刻使用它。

Input

  • 输入的第一行为一个整数 \(T(1<=T<=5)\),表示数据的组数。
  • 对于每一组数据,第一行是两个由空格隔开的整数 \(N\)\(M(2<=N,M<=40)\)
  • 接下来的 \(N\) 行每行将是一个包含 \(M\) 个字符的字符串。
  • 每一个字符可以是一个整数 \(X(0<=X<=9)\) 或一个“\(*\)”或一个“\(\#\)”。
    • 如果是一个整数 \(X\) 表示这个格子有 \(X\) 单位的矿石,当你的矿车经过这里时可以把它们全部采集走。
    • 如果是“ \(*\) ”则表示在这个格子有一个传送门。
    • 如果是“ $# $ ” 则表示在这个格子里是有障碍物而不能到达的。
  • 矿车的起始位置一定没有障碍物。
  • 就上面的地图显示的,如果地图中一共有 \(K\) 个“ $ *$”。则接下来的 \(K\) 行描述每个传送门传送的目的地,按照传送门的位置从北到南,从西到东的顺序给出(即逐行给出)。(传送门的目的地的横纵坐标是从 \(0\)\(N-1,0\)\(M-1\) 的)

Output

  • 对于每一组数据,你应该输出你最多能够获得多少单位的矿石。

Sample Input

1
2 2
11
1*
0 0

Sample Output

3

Hint

  • 提示:你的终点是任意的,你不能走到地图外,传送门是不会把你传送到地图外的。
  • 来源:

分析

  • 如果没有传送门,显然就是一个裸裸的 \(dfs\)\(bfs\) 而已,现在就是在此基础上加传送门,我们想想,传送门能产生什么变化?很显然如果传送门往回跳的时候会产生环,根据题意,环上的点是都可以取的,很容易想到了缩点,这些思路就没明确了,先建图,此题建图稍有麻烦:
    • 把二维变一维,比如:\((x,y)\) 表示 \(x\) 行, \(y\) 列点(编号从 \(0\) 开始),映射成节点编号为:\(x*m+y\),(矩阵有 \(m\) 列)。
    • 对每一个点如果相邻点为障碍就不要建边,如果有传送门就需要建边,传送门所在点的点权为 \(0\) ,传送门向目标建边,(这里的边均为有向边)。
  • 图建好后缩点建新图,新图为 \(DAG\) 图,在新图上做一遍 \(dfs\) 维护其子节点到根的最大距离,注意记忆化。

Code

#include <bits/stdc++.h>
const int maxn=45*45,maxe=100005;
struct Edge{
    int to,next;
}a[maxe],b[maxe];
int m,n,tot,num,len;
int belong[maxn],dis[maxn],Low[maxn],Dfn[maxn];
int head[maxn],rhead[maxn],cost[maxn],c[maxn];
bool vis[maxn];
char Map[50][50];
std::stack <int> q;
void Insert(int x,int y){a[++len].to=y;a[len].next=head[x];head[x]=len;}
void Rinsert(int x,int y){b[++len].to=y;b[len].next=rhead[x];rhead[x]=len;}
void Init(){
    num=len=tot=0;
    memset(vis,0,sizeof(vis));
    memset(dis,-1,sizeof(dis));
    memset(Dfn,0,sizeof(Dfn));
    memset(head,-1,sizeof(head));
    memset(rhead,-1,sizeof(rhead));
    memset(belong,0,sizeof belong);
    memset(c,0,sizeof(c));
}
void Tarjan(int u){
    Low[u]=Dfn[u]=++num;vis[u]=1;q.push(u);
    for(int i=head[u];i!=-1;i=a[i].next){
        int v=a[i].to;
        if(!Dfn[v]){
            Tarjan(v);
            Low[u]=std::min(Low[u],Low[v]);
        }
        else if(vis[v])Low[u]=std::min(Low[u],Dfn[v]);
    }
    if(Low[u]==Dfn[u]){
        int t;++tot;
        do{
            t=q.top();q.pop();vis[t]=0;c[tot]+=cost[t];belong[t]=tot;
        }while(t!=u);
    }
}

int Dfs(int x){
    if(dis[x]!=-1)return dis[x];
    int Max=0;
    for(int i=rhead[x];i!=-1;i=b[i].next){
        Max=std::max(Max,Dfs(b[i].to));
    }
    return dis[x]=Max+c[x];
}
void Solve(){
    int T;scanf("%d",&T);
    while(T--){
        Init();     
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;++i)scanf("%s",Map[i]);        
        for(int i=0;i<n;++i){
            for(int j=0;j<m;++j){
                int x=i*m+j,y=x+1;//二维变一维,x当前点,y右边点
                if(Map[i][j]=='#')continue;
                if(Map[i][j]=='*')cost[x]=0;//传送点金矿为0
                else cost[x]=Map[i][j]-48;
                if(j+1<m&&Map[i][j+1]!='#')Insert(x,y);//右边不是障碍就建边
                y=(i+1)*m+j;//正下
                if(i+1<n&& Map[i+1][j]!='#')Insert(x,y);
                if(Map[i][j]=='*'){//处理传送点
                    scanf("%d%d",&x,&y);
                    if(Map[x][y]!='#')Insert(i*m+j,x*m+y);
                }
            }
        }
        while(!q.empty())q.pop();
        Tarjan(0);
        
        len=0;
        for(int i=0;i<m*n;++i){
            for(int j=head[i];j!=-1;j=a[j].next){
                int x=belong[i],y=belong[a[j].to];
                if(x!=y)Rinsert(x,y);
            }
        }
        printf("%d\n",Dfs(belong[0]));
    }
}
int main(){
    Solve();
    return 0;
}
posted @ 2020-07-26 13:15  ♞老姚♘  阅读(468)  评论(0编辑  收藏  举报