题目大意
在一个N*M的地图上 (2≤N,M≤40) ,每个格子有三种情况:
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;
}