题目链接:传送门
万万没想到啊!第三题就把我难住了,果然是PAT教我做人,大概了解甲级的难度了,好好刷题吧我个呆瓜
是我太笨了,想不到这么巧妙的方法,说出来呢其实就是迪杰斯特拉单源最短路径算法的一点扩展和变种,可能没见过所以就不会做!!!!!!
哦对,这题不是我自己做的,是看了大佬的博客,以下是链接,自取自取:传送门
但其实大佬没有过多的解释,建议看不懂代码的可以看看我的解释!!!(卑微
这里是迪杰斯特拉算法的大致过程,心里明白的可以不用看, 不明白的可以大致看下复习下
(写的非常粗糙,不适合新手学习观看)
是这样,迪杰斯特拉算法的过程大概就是
①dist数组里面存储从起点到其他所有点的最短距离。dist[i]表示从源点 s 到 i 的最短距离,初始化为图上的边权,从 s 到 i 有边则初始化为边权,无边则初始化为无穷大。
②在所有未被访问过的点中选择出dist值最小的点k ③将k点加入到最小路径权值已知的点集合中来(代码中表现为将其标记为一个已访问过的点)
④更新。更新什么呢?因为我们刚刚将一个新的点k加入到了最小路径权值已知的集合中来,那因为这个k之前是没有的,那是不是有可能存在从源点 s 到 k ,再从 k 到某个点 i 的距离会因为k这个点的加入距离变小呢,答案是有可能的,所以要对所有未被访问过的点进行检查更新,如果能够通过k缩短路径,就要更新dist数组的值。这样每次选出一个dist最小的值,选n-1次,就可以把整个图全覆盖,完成单源最短路径算法。
这道题目的变形在于,可以利用更新过程完成这道题目所需要的到的信息。
这道题目需要得到的信息是:从源点S到终点T的最短路径有几条,这些路径中,权值最大的是多少(一条路径的权值为该条路径上所有点权值之和)。
①要统计最短路径的条数,则需要用到num数组统计从源点到点 i 的最短路径条数,我们想想最短路径是不是在更新的时候,可能会更新得到更短的路径,那每一次更新都是通过dist值最小的点 K 来更新,那我们想想,从源点 S 到被更新的点 i 的最短路径数,是不是就等于从源点 S 到点 K 的最短路径数。相当于num[i]=num[k]。
原因就是,从s到k有 num[k]条最短路,从k到i有一条路(就是从k到i的边嘛),问从s到i有多少条最短路?很简单就是1*num[k]=num[k]。所以得到以上结论!
②要统计这些路径中哪条路径权值最大。
方法跟上面很像,就是在每次更新路径的时候把存储路径权值的数组val也一并更新,更新为val[k]+a[i] (a[i]代表的是点权,val[i]代表的是从源点到 i 的最小路径的最大权值)。
但是!!有一个问题需要注意!!
迪杰斯特拉的时候,只有路径长度变小我们才需要更新,因为我们当时只关心长度,但是现在不一样了,我们还需要在路径长度相同的情况下选择权值更大的,所以要加一句,当路径长度相等的时候,判断一下权值是否变大了,如果变大了,就更新为当前的权值,同时num也要加上num[k],因为路径相等说明,以k为中介到达i也是最短路径,所以要把num[k]这么多种路径数加上去,即num[i]+=num[k]。
这里num数组的处理不同于上面的是因为上面是我发现了更短的路径,那之前的num根本就不能再用了,因为他不是最小路径了,所以直接赋值,把之前的路径数覆盖掉,这里不一样,这里是,我现在也是最短路径数,我是答案的一部分,所以要跟之前的num合在一起更新num。
啊,说了罗里吧嗦一大堆,我好lui
不说了,不懂的结合代码看看吧,溜了,多刷题多见见世面!
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<stack>
#define inf 4000000000000000000
using namespace std;
typedef long long ll;
const ll maxn=1e6+50;
const ll mod=1e9+7;
const double eps=1e-9;
ll a[550],val[550],num[550],mp[550][550],dist[550];
bool vis[550];
int main()
{
ll n,m,c1,c2;
scanf("%lld %lld %lld %lld",&n,&m,&c1,&c2);
for(ll i=0;i<n;i++)
{
scanf("%lld",&a[i]);
}
for(ll i=0;i<=n;i++)
{
for(ll j=0;j<=n;j++)
{
mp[i][j]=4e18;
}
}
for(ll i=1;i<=m;i++)
{
ll u,v,w;
scanf("%lld %lld %lld",&u,&v,&w);
mp[u][v]=w;
mp[v][u]=w;
}
for(ll i=0;i<505;i++)
{
dist[i]=4e18;
}
dist[c1]=0;
num[c1]=1;
val[c1]=a[c1];
for(ll o=1;o<=n;o++)
{
ll minn=4e18,k=-1;
for(ll i=0;i<n;i++)
{
if(!vis[i]&&minn>dist[i])
{
minn=dist[i];
k=i;
}
}
if(k==-1)break;
vis[k]=1;
for(ll i=0;i<n;i++)
{
if(mp[k][i]!=inf&&!vis[i])
{
if(dist[i]>dist[k]+mp[k][i])
{
dist[i]=dist[k]+mp[k][i];
num[i]=num[k];
val[i]=val[k]+a[i];
}
else if(dist[i]==dist[k]+mp[k][i])
{
if(val[k]+a[i]>val[i])
{
val[i]=val[k]+a[i];
}
num[i]+=num[k];
}
}
}
}
printf("%lld %lld\n",num[c2],val[c2]);
//scanf("%lld",&n);
return 0;
}