题目大意:求给定的DAG中最短路&最短路-1的路径数,M(M<=10000)条信息。
题目分析:题目要求最短路和长度最短路-1的路径数目,看到网上说A*会爆,蒟蒻暗自开心反正也不会。
言归正传,这道题目很经典,很考察对Dijkstra的理解。
分析一下的话,这道题的大致算法就是对一个点求两个Dist,两个cnt(记录最短路的数目),其中Dist[ ][0]表示最短距离,Dist[ ][1]表示次短的距离,Cnt同理。
然后在我们Dijkstra的时候,计数是这样的。
我们将 到一点的新的路径长度设为 D.
1、当D小于最短距离时:更新最短距离,计数=当前新cnt,更新次短路长,计数。
2、当D等于最短路距离时:计数。
3、当D大于最短路距离时并且小于次短路距离时:更新次短路的距离和计数。
4、当D等于次短路长度时:次短路计数。
如下代码(不知为何我不敢入目的代码wa了,于是粘了一个友人的代码,思路一样,并带注解):
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <queue>
#include<iostream>
#define VM 1005
#define EM 10010
using namespace std;
const int inf = 0x3f3f3f3f;
int head[VM],cnt[VM][2],dist[VM][2],vis[VM][2];
int e,src,des,n,m;
struct E
{
int to,w,nxt;
} edge[EM];
void addedge (int cu,int cv,int cw)
{
edge[e].to = cv;
edge[e].w = cw;
edge[e].nxt = head[cu];
head[cu]=e++;
}
int dij ()
{
int i,j,u,min,flag;
memset (dist,0x3f,sizeof(dist));
memset (vis,0,sizeof(vis));
memset (cnt,0,sizeof(cnt));
dist[src][0] = 0;
cnt[src][0] = 1;
for (i = 1; i < 2*n; i ++)//记录最短路和次短路,所有至少循环2*n-1次
{
min = inf;
for (j = 1; j <= n; j ++)
if (!vis[j][0]&&dist[j][0] < min)
{
u = j;
flag = 0;//flag记录是次短路还是最短路
min = dist[j][0];
}
else if (!vis[j][1]&&dist[j][1] < min)
{
u = j;
flag = 1;
min = dist[j][1];
}
if (min == inf)
break;
vis[u][flag] = 1;
for (j = head[u];j!= -1; j = edge[j].nxt)
{
int v = edge[j].to;
int w = edge[j].w + min;
if (dist[v][0] > w)
{
dist[v][1] = dist[v][0];
cnt[v][1] = cnt[v][0];
dist[v][0] = w;
cnt[v][0] = cnt[u][flag];
}
else if (dist[v][0] == w)
cnt[v][0] += cnt[u][flag];
else if (dist[v][1] > w)
{
dist[v][1] = w;
cnt[v][1] = cnt[u][flag];
}
else if (dist[v][1] == w)
cnt[v][1] += cnt[u][flag];
}
}
if (dist[des][0] + 1 == dist[des][1])//是否存在差为一
cnt[des][0] += cnt[des][1];
return cnt[des][0];
}
int main ()
{
int T,u,v,w;
scanf ("%d",&T);
while (T --)
{
scanf ("%d%d",&n,&m);
memset (head,0xff,sizeof(head));
e = 0;
while (m --)
{
scanf ("%d%d%d",&u,&v,&w);
addedge (u,v,w);
}
scanf ("%d%d",&src,&des);
int ans = dij();
printf ("%d\n",ans);
}
//while(1);
return 0;
}