POJ 3463 Sightseeing Dijkstra最短路&最短路-1的路径数(计数)

题目大意:求给定的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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值