poj 3592 Instantaneous Transference 强连通缩点+在DAG上dp求最长路

题意:

给一个地图,在每一点只能向下或向右走,其中标有‘*’点上有传送门可以传送到他指定的位置,标有数字的点上代表在该点可以得到的矿石,标有‘#’的点代表这点不能走,求从(0,0)点开始能最多获得多少矿石。

分析:

因为有传送门所以图中可能有环,缩点后在所得的DAG中求最长路就可以了。

代码:

//poj 3592
//sep9
#include <iostream>
#include <vector>
using namespace std;
const int maxN=64;
char g[maxN][maxN];
int map[maxN][maxN];
int vis[maxN*maxN],dfn[maxN*maxN],sum[maxN*maxN],dp[maxN*maxN];
int n,N,M,cnt,scc;
vector<int> graph[maxN*maxN],ngraph[maxN*maxN],dag[maxN*maxN];

void addegde(int u,int v)
{
	graph[u].push_back(v);
	ngraph[v].push_back(u);		
}

void dfs(int k)
{
	vis[k]=1;
	for(int i=graph[k].size()-1;i>=0;--i)
		if(!vis[graph[k][i]])
			dfs(graph[k][i]);
	dfn[++cnt]=k;
}

void ndfs(int k)
{
	vis[k]=scc;
	for(int i=ngraph[k].size()-1;i>=0;--i)
		if(!vis[ngraph[k][i]])
			ndfs(ngraph[k][i]);	
}

void kosaraju()
{
	memset(vis,0,sizeof(vis));
	cnt=0;
	for(int i=0;i<n;++i)
		if(!vis[i])
			dfs(i);	
	memset(vis,0,sizeof(vis));
	scc=0;
	for(int i=n;i>=1;--i)
		if(!vis[dfn[i]]){
			++scc;
			ndfs(dfn[i]);
		}
}

void search(int u)
{
	if(dp[u]!=-1)
		return ;
	else{
		int maxx=0;
		for(int i=dag[u].size()-1;i>=0;--i){
			int v=dag[u][i];
			search(v);
			maxx=max(maxx,dp[v]);
		}
		dp[u]=maxx+sum[u];
	}
	return ;	
}

int main()
{
	int cases,i,j;
	scanf("%d",&cases);
	while(cases--){
		scanf("%d%d",&N,&M);	
		n=N*M;
		for(i=0;i<=N;++i)
			for(j=0;j<=M;++j)
				map[i][j]=i*M+j;		
		for(i=0;i<N;++i)
			scanf("%s",g[i]);

		for(i=0;i<=n;++i)
			graph[i].clear(),ngraph[i].clear(),dag[i].clear();
		
		for(i=0;i<N;++i)
			for(j=0;j<M;++j){		
				if(g[i][j]!='#'){
					if(i+1<N&&g[i+1][j]!='#')
						addegde(map[i][j],map[i+1][j]);
					if(j+1<M&&g[i][j+1]!='#')
						addegde(map[i][j],map[i][j+1]);
					if(g[i][j]=='*'){
						int r,c;
						scanf("%d%d",&r,&c);
						if(g[r][c]!='#')
							addegde(map[i][j],map[r][c]);
					}
				}
			}				
		kosaraju();
		for(i=0;i<n;++i)
			for(j=graph[i].size()-1;j>=0;--j){
				int u=i,v=graph[i][j];
				if(vis[u]!=vis[v])
					dag[vis[u]].push_back(vis[v]);
			}
	
		memset(sum,0,sizeof(sum));
		for(i=0;i<N;++i)
			for(j=0;j<M;++j)
				if(g[i][j]<='9'&&g[i][j]>='0'){
					int p=map[i][j];
					sum[vis[p]]+=g[i][j]-'0';
				}
		int start=vis[map[0][0]];			
		memset(dp,-1,sizeof(dp));
		search(start);	
		printf("%d\n",dp[start]);
	}
	return 0;	
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值