51nod 1459 迷宫问题 (Dijkstra或SPFA求最短路)

传送门51nod 1459

题目大意:有一迷宫由多个房间组成,房间之间有连接的道路,进入每个房间都会有相应的奖励,问从一源点到汇点的最短路径是多少,在此最短路径下可以获得的最大奖励是多少。


Input示例

3 2 0 2
1 2 3
0 1 10
1 2 11

Output示例

21 6

思路:既然是求最短路径的问题肯定是要用单源最短路算法了,我们可以求出源点到任意点的最短路。不过题目中还要求最大奖励,而这个最大奖励是在最短路径的前提下的,如果当前路比已知的最短路径更短,则最大奖励一定更新;如果和已知最短路长度相等且奖励更大,则也要更新,其他状态不必更新。


具体实现:可以用求最短路的Dijkstra算法或SPFA算法实现,用dis数组保存源点到任意点的最短路,类似的,用val数组保存从源点到每个点的最短路径下的最大奖励。


注意

1.源点和汇点不是确定的 0 和 n-1……在这被坑了好多次……

2.使用链式前向星存储图会超时……这个实在想不通为什么,可能是我的姿势有问题……

3.Dijkstra算法中,源点先不能标记访问过,因为后面要用源点更新当前最短路和已知最短路相等的情况。


Dijkstra算法版:

#include<stdio.h>
#include<string.h>
#define inf 0x3f3f3f3f

int n,m;
int vis[505],dis[505],val[505];
int a[505],map[505][505];

int dijkstra(int s)
{
	int i,j,min,pos;
	memset(vis,0,sizeof(vis)); //全标记为未处理
	memset(val,0,sizeof(val));
	for(i=0;i<n;i++)
		dis[i]=map[s][i];
    dis[s]=0;
    //vis[s]=1;    源点一开始不可以标记为访问过,否则无法处理到第38行的代码 
	val[s]=a[s];
	//算法核心代码
	for(i=0;i<n-1;i++)
	{ //进行n-1次处理
		min=inf;
		//0不一定是开始的节点 
		for(j=0;j<n;j++) //寻找未处理的点中路径最短的
			if(!vis[j] && dis[j]<min)
			{
				min=dis[j];
				pos=j;
			}
		vis[pos]=1;
		for(j=0;j<n;j++) //用该节点更新其他未处理节点的最短路径
			if(dis[j]>dis[pos]+map[pos][j])
			{ //如果当前路径更短,则更新最短路和最大奖励值 
				val[j]=val[pos]+a[j];
				dis[j]=dis[pos]+map[pos][j];				
			}
			else if(dis[j]==dis[pos]+map[pos][j])
			{ //如果当前路径和已知最短路一样,则只更新最大奖励 
				if(val[j]<val[pos]+a[j]) val[j]=val[pos]+a[j];						
			}
	}
}

int main()
{
	int i,j,s,e,u,v,w;	
	scanf("%d%d%d%d",&n,&m,&s,&e);
	for(i=0;i<=n;i++)
		for(j=0;j<=n;j++)
			map[i][j]=inf;
	for(i=0;i<n;i++) scanf("%d",&a[i]);
	for(i=0;i<m;i++)
	{
		scanf("%d%d%d",&u,&v,&w);
		map[u][v]=w;
		map[v][u]=w;
	}
	dijkstra(s);
	printf("%d %d\n",dis[e],val[e]);
	return 0;
}

SPFA算法版:

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<queue>
#define inf 0x3f3f3f3f
using namespace std;

int dis[505],val[505];
int vis[505]; //标志该节点是否在队列中
int a[505],mp[505][505];

void SPFA(int v,int n)
{//参数:源点 / 点的个数 
	int i,top;
	//初始化
	memset(val,0,sizeof(val));
	memset(vis,0,sizeof(vis));
	for(i=0;i<n;i++)
		dis[i]=mp[v][i];
	dis[v]=0;	
	val[v]=a[v];   
	queue<int> Q;
	Q.push(v); //将源点入队
	vis[v]=1;
	while(!Q.empty())
	{
		top=Q.front();
		Q.pop();
		vis[top]=0;  //标志队首元素q不在队列中
		for(i=0;i<n;i++)
		{
			if(mp[top][i]==inf) continue;
			if(dis[top]+mp[top][i]<dis[i]) //如果源点到q节点的距离+q节点到i节点的距离<源点到i节点的距离
			{ //如果当前路径更短,则更新最短路和最大奖励值 
				val[i]=val[top]+a[i];
				dis[i]=dis[top]+mp[top][i]; //进行松弛操作
				if(!vis[i])       //如果当前节点i不在队列中
				{
					vis[i]=1;     //标志在队列中
					Q.push(i);    //将当前节点i入队					
				}
			}
			else if(dis[top]+mp[top][i]==dis[i]) //如果源点到q节点的距离+q节点到i节点的距离<源点到i节点的距离
			{ //如果当前路径和已知最短路一样,则只更新最大奖励 
				if(val[i]<val[top]+a[i])
				{
					val[i]=val[top]+a[i];
					if(!vis[i])       //如果当前节点i不在队列中
					{
						vis[i]=1;     //标志在队列中
						Q.push(i);    //将当前节点i入队					
					}
				}
			}
		}
	}
}

int main()
{
	int i,j,n,m,s,e,u,v,w;
	scanf("%d%d%d%d",&n,&m,&s,&e);
	for(i=0;i<n;i++)
		for(j=0;j<n;j++)
			mp[i][j]=inf;
	for(i=0;i<n;i++) scanf("%d",&a[i]);
	for(i=0;i<m;i++)
	{
		scanf("%d%d%d",&u,&v,&w);
		mp[u][v]=w;
		mp[v][u]=w;
	}
	SPFA(s,n);
	//for(i=0;i<n;i++) printf("%d ",val[i]);
	//printf("\n");
	printf("%d %d\n",dis[e],val[e]);
	return 0;
}

    原文作者:迷宫问题
    原文地址: https://blog.csdn.net/zuzhiang/article/details/78301295
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞