版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_41661919/article/details/84892909
题目链接:
题意:求在一张无向图中用到达各点的最短路生成的树一共可以有多少颗。
分析:区别最小生成树,用最短路构成的数不一定是最小生成树,最小生成树也不一定由最短路们构成。
运用乘法原理,我们只需要求出到达图中某点的路径中路径长度等于最短路长度的有多少条,并将其叠乘进ans中即可。
所以现用二叉堆优化的dijkstra求“1”到各点的最短路长度d【N】;然后:
怎么求出到达某点的这样的路径有多少条呢?我们只要遍历一遍某点前面的点们,看看有没有符合要求d【前面的点】+v(边权值)=d【“1”点到该点的最短路】的点即可。前面的点指的是与当前点有边相连且在到达当前点之前到达的点。我们可以在求出“1”点到达每个点的最短路之后根据dis对这些点进行排序。然后可达关系的后面的点与前面点的先后关系就是在排序后序列中的关系。(PS:遍历所有点、二层循环遍历与当前点连接的所有点就好了,他后面的点的d加上两点间边的权值肯定不会是到达当前点的最短路,所以一定不会被选择,emm,,,,,,也就说没必要排序,可能时间复杂上稍有差异吧,不过应该也不大)。
AC代码:
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include<algorithm>
#include <set>
#include <queue>
#include <stack>
#include<vector>
#include<map>
#include<ctime>
#define ll long long
using namespace std;
const int N=100010,M=1000010;
int head[N];
int ver[M];
int edge[M];
int Next[M];
bool v[N];
int tot;
int d[N];
priority_queue< pair<int,int> >Q;
void add(int x,int y,int z)
{
ver[++tot]=y;
edge[tot]=z;
Next[tot]=head[x];
head[x]=tot;
}
void dijkstra()
{
memset(d,0x3f,sizeof(d));
memset(v,0,sizeof(v));
d[1]=0;
Q.push(make_pair(0,1));
while(Q.size())
{
int x=Q.top().second;
Q.pop();
if(v[x])continue;
v[x]=1;
for(int i=head[x];i;i=Next[i])
{
int y=ver[i],z=edge[i];
if(d[y]>d[x]+z)
{
d[y]=d[x]+z;
Q.push(make_pair(-d[y],y));
}
}
}
}
int num[10010];
int main()
{
int n,m;
while(cin>>n>>m)
{
tot=0;
memset(head,0,sizeof(head));
memset(ver,0,sizeof(ver));
memset(edge,0,sizeof(edge));
memset(Next,0,sizeof(Next));
for(int i=1;i<=m;++i)
{
int x,y,l;
cin>>x>>y>>l;
add(x,y,l);//dijkstra用于无向图,所以,别忘了建双向边;
add(y,x,l);
}
dijkstra();//求最短路
memset(num,0,sizeof(num));
num[1]=1;
for(int i=2;i<=n;++i)//求到达某点路径中长度为最短路的路径数目;
{
for(int j=head[i];j;j=Next[j])
{
int pp=ver[j],vv=edge[j];//邻接表常规操作
if(d[i]==d[pp]+vv)num[i]++;
}
}
ll ans=1;
for(int i=1;i<=n;++i)
{
ans=ans*num[i]%2147483647;//叠乘答案;
}
cout<<ans<<endl;
}
}
The end;