poj 3592(强连连通分量+缩点+重建图形+spfa求最长路)

88 篇文章 0 订阅

                  感觉这道题很水,虽然过的人不多,觉得很恶心,思路还是懂得,但写的时候很麻烦,主要是代码的功底不好,,,,这题可以练代码量。自己写了你才懂得的,搞了半天,,,,
        回正题:给出一个n*m的矩阵,每个格子可能放有0~9,#,*。0~9代表金矿的数量,#代表阻碍,不能够走,*代表传送点,可以瞬间传送到特定的位置,对于传送点的传送功能可以无限使用,也可以不用。初始时你开着一辆坦克在最左上角,你只能往右或者往下走,如果该格子上放有金矿的话你可以把他拿走,但只能拿一次。问最后你能够拿到最多的金矿的数量。
       思路 :只能往右或者往下走,将这些节点构成一个有向图,但是因为有传送门的存在这个图中可能会有环,在一个强联通分支内,想到用tarjan进行缩点,环内所有的价值都能拿到,这时候有向图中没有环而变成了一个树,然后在这个树中找最长路径就可以了。
#include<cstdio>
#include<cstdlib>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
const int NN = 1605;
struct Edge
{
  int v,w;
};
int n,m;
char map[50][50];
int dfn[NN],low[NN],index,scc,belong[NN],dis[NN];
int score[NN],w[NN];
int magic[NN],cnt;
int s[NN],top;
bool instack[NN],vis[NN];
vector<int> Grap[NN];
vector<Edge> GNew_[NN];


void init()
{
 scanf("%d%d",&n,&m);
 getchar();
 cnt = 0;
 for(int i=0;i<NN;i++)
 {
  Grap[i].clear();
  GNew_[i].clear();
 }
 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 k = i*m+j;
    if(map[i][j]=='#')
    score[k] = -1;
    else
    {
      if(map[i][j]=='*')
      {
       magic[cnt++] = k;
        score[k] = 0;
      }
      else
      score[k] = map[i][j]-'0';
         if(i+1<n && map[i+1][j]!='#')
         Grap[k].push_back(k+m);
         if(j+1<m && map[i][j+1]!='#')
         Grap[k].push_back(k+1);
     }
    }
  for(int i=0;i<cnt;i++)
  {
   int p,q,v;
   scanf("%d%d",&p,&q);
   v = p*m+q;
   if(score[v]==-1) continue;
   Grap[magic[i]].push_back(v);
  }
}


void tarjan(int u)
{
  int v;
  dfn[u] = low[u] = ++index;
  s[++top] = u;
  instack[u] = true;
  for(int i=0;i<Grap[u].size();i++)
  {
   v = Grap[u][i];
   if(!dfn[v])
   {
    tarjan(v);
    low[u] = min(low[v],low[u]);
   }
   else if(instack[v])
   low[u] = min(low[u],dfn[v]);
  }
   if(dfn[u]==low[u])
   {
     w[++scc] = 0;
     while(true)
      {
        v = s[top--];
        instack[v] = false;
        belong[v] = scc;
        w[scc] += score[v];
        if(v==u)
          break;
      }
    }
}


void rebuilt()
{
  top = scc = index = 0;
  memset(instack,false,sizeof(instack));
  memset(dfn,0,sizeof(dfn));
  for(int i=0;i<n*m;i++)
    if(!dfn[i])
       tarjan(i);


  for(int u=0;u<n*m;u++)
  for(int i=0;i<Grap[u].size();i++)
  {
    int v = Grap[u][i];
     if(belong[u]!=belong[v])
     {
         Edge e = {belong[v],w[belong[v]]};
          GNew_[belong[u]].push_back(e);
     }
  }
   Edge e = {belong[0],w[belong[0]]};
   GNew_[0].push_back(e);
}


int spfa(int s)
{
 memset(dis,-1,sizeof(dis));
 memset(vis,false,sizeof(vis));
 dis[s] = 0;
 vis[s] = true;
 queue<int> q;
 q.push(s);
while(!q.empty())
{
 int u = q.front();
 q.pop();
 vis[u] = false;
 for(int i=0;i<GNew_[u].size();i++)
 {
  int v = GNew_[u][i].v;
  int w = GNew_[u][i].w;
  if(dis[v]<dis[u]+w)
  {
   dis[v] = dis[u]+w;
   if(!vis[v])
   {
   q.push(v);
   vis[v] = false;
   }
  }
 }
}
int ans = 0;
for(int i=0;i<=scc;i++)
ans = max(ans,dis[i]);
return ans;
}


int main()
{
 int T;
 scanf("%d",&T);
 while(T--)
 {
  init();
  rebuilt();
  int answ = spfa(0);
  printf("%d\n",answ);
 }
 return 0;
}
        


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值