POJ 3592 Instantaneous Transference (强连通分量 缩点 spfa最长路)

POJ 3592

题目大意

在一个N*M的地图上 (2N,M40) ,每个格子有三种情况:

X:表示这个地方有X个金币

*:这个地方是一个虫洞,可以瞬移到另一个地方(会给出瞬移到哪个地方),也可以选择不瞬移

#:这个地方是石头不能走

现在从左上角出发,每一步只能往右或下走,问最多能得到多少金币

分析

首先容易想到的是将这个矩形地图转化成有向图的问题。

那么现在的问题就是:

给定一个有向图,节点上面有权值,给定一个起点并从这个点出发问最多能得到的权值总和。

很明显,如果有向图上面有环,一旦走到上面一个点所有点都将会被走到,那么这个环就可以缩成一个点将环上的权值相加作为点的权值。

那么现在就变成了一个不成环的有向图,再求一个无环有向图的最长路即可

总结

在一个细节上面错了导致WA了几次,Init的时候写成了memset(scc_score,0,sizeof(scc_int));
最后测试数据的时候发现了是多个数据之间某些值发生了继承但还是找了好久才找出是这个地方写错了,以后在写代码的时候就应该仔细一点。
虽然题目不难但是代码量还是挺多的

代码

/*
N*M地图从上到下是1到N,从左到右是1到M
*/
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<map>
#include<algorithm>
#include<set>
#include<stack>
using namespace std;
const int MAXN=2505;
int N,M;
char mat[50][50];
int dfn[MAXN];
int low[MAXN];
int vis[MAXN];
int belong[MAXN];
int score[MAXN];
int scc_score[MAXN];//强连通分量得分
int scc_cnt;//强连通分量数
int ti;//时间戳
stack<int> St;
struct Edge
{
      int v;
      int next;
}edge[50000],edge2[50000];
int edgecount;
int edgecount2;
int head[MAXN];
int head2[MAXN];
void Init()
{
    memset(head,-1,sizeof(head));
    memset(head2,-1,sizeof(head2));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(vis,0,sizeof(vis));
    memset(belong,0,sizeof(belong));
    memset(score,0,sizeof(score));
    memset(scc_score,0,sizeof(scc_score));
    edgecount=0;
    edgecount2=0;
    ti=0;
    scc_cnt=0;
    while(!St.empty())St.pop();
}
void Add_edge(int u,int v)
{
      edge[++edgecount].v=v;
      edge[edgecount].next=head[u];
      head[u]=edgecount;
}
int Get_num(int x,int y)//得到坐标为(x,y)的位置的编号
{
    return (x-1)*M+y;
}
bool Is_out(int x,int y)
{
      if(x<1 || x>N || y<1 || y>M)return 1;
      return 0;
}
void Build()//建图
{
    int u,v;
    int a,b;
    for(int i=1;i<=N;i++)
    {
          for(int j=1;j<=M;j++)
          {
                u=Get_num(i,j);
                if(mat[i][j]=='#')continue;
                else if(mat[i][j]=='*')
                {
                      scanf("%d%d",&a,&b);
                      a++;b++;
                      if(mat[u][Get_num(a,b)]!='#')Add_edge(u,Get_num(a,b));
                      //cout<<"("<<u<<","<<Get_num(a,b)<<")"<<endl;
                      score[u]=0;
                }
                else score[u]=(int)mat[i][j]-(int)'0';//X
                if(!Is_out(i+1,j) && mat[i+1][j]!='#'){Add_edge(u,Get_num(i+1,j));}//cout<<"("<<u<<","<<Get_num(i+1,j)<<")"<<Get_num(2,1)<<endl;}
                if(!Is_out(i,j+1) && mat[i][j+1]!='#'){Add_edge(u,Get_num(i,j+1));}//cout<<"("<<u<<","<<Get_num(i,j+1)<<")"<<endl;}
          }
    }
}
void Tarjan(int u)
{
      dfn[u]=low[u]=++ti;
      St.push(u);
      vis[u]=1;
      for(int k=head[u];k!=-1;k=edge[k].next)
      {
            int v=edge[k].v;
            if(!dfn[v])//v没有访问过
            {
                  Tarjan(v);
                  low[u]=min(low[u],low[v]);
            }
            else if(vis[v]==1)//v被访问过且还在栈里
                  low[u]=min(low[u],dfn[v]);
      }
      if(low[u]==dfn[u])//出栈
      {
           int x;
           ++scc_cnt;
           while(1)
            {
                  x=St.top();
                  St.pop();
                  vis[x]=0;
                  belong[x]=scc_cnt;
                  if(x==u)break;
            }
      }
}
void Spfa(int u)
{
      int bj[MAXN];//bj为1表示在队列里面
      int max_score[MAXN];//max_score[i]表示到达节点i得到的最大得分
      memset(bj,0,sizeof(bj));
      memset(max_score,-1,sizeof(max_score));
      queue<int> Q;
      max_score[u]=scc_score[u];
      Q.push(u);
      bj[u]=1;
      while(!Q.empty())
      {
            u=Q.front();
            Q.pop();
            for(int k=head2[u];k!=-1;k=edge2[k].next)
            {
                  int v=edge2[k].v;
                  if(max_score[u]+scc_score[v]>=max_score[v])
                  {
                        max_score[v]=max_score[u]+scc_score[v];
                        if(bj[v]==0)
                        {
                              Q.push(v);
                              bj[v]=1;
                        }
                  }
            }
            bj[u]=0;
      }
      int ans=0;
      for(int i=1;i<=scc_cnt;i++)
      {
            ans=max(ans,max_score[i]);
      }
      cout<<ans<<endl;
}
void Add_edge2(int u,int v)
{
      edge2[++edgecount2].v=v;
      edge2[edgecount2].next=head2[u];
      head2[u]=edgecount2;
}
void Solve()
{
      Tarjan(1);
      //得到缩点后每个强连通分量的分数
      for(int i=1;i<=N*M;i++)
            scc_score[belong[i]]+=score[i];
      //cout<<"a"<<scc_cnt<<endl;
      //缩点后重新建图
      for(int u=1;u<=N*M;u++)
      {
            for(int k=head[u];k!=-1;k=edge[k].next)
            {
                  int v=edge[k].v;
                  if(belong[u]!=belong[v])
                  {
                       Add_edge2(belong[u],belong[v]);
                  }
            }
      }
      Spfa(belong[1]);//搜索一遍得到到每个缩点后节点的最大得分,并输出答案
}
int main()
{
    char c;
    int T;
    scanf("%d",&T);
    while(T--)
    {
          scanf("%d%d",&N,&M);
          for(int i=1;i<=N;i++)
          {
                for(int j=1;j<=M;j++)
                {
                      scanf("%c",&c);
                      while(c=='\n')scanf("%c",&c);
                      mat[i][j]=c;
                }
          }
          Init();//初始化
          Build();//建图
          Solve();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值