传送门: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;
}