POJ 3463 Sightseeing

POJ 3463 Sightseeing

最短路变形,次短路

传送门:POJ


题意

已知一张图(单向边),起点S和终点F,求从S到F的最短路和比最短路长1的路径的条数之和。


思路

同时统计最短路和次短路,最后判断:如果次短路长度比最短路大1,那输出和;否则只输出最短路。
dijkstra基本框架不变,距离,方案数,vis数组都开二维,第二维是0或1,表示最短路/次短路。
松弛条件变化:

  1. 当前处理的距离小于对应最短路的距离,用最短路更新次短路,再用当前这个更新最短路;
  2. 当前处理的距离等于最短路的距离,更新方案数;
  3. 小于次短路距离,更新次短路;
  4. 等于次短路,更新次短路方案数。

每次松弛后把新的点加入队列。


代码

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;

const int MAXN=20007;
const int oo=1000000007;
typedef long long LL;
struct Graph{
    int next;
    int to;
    int cost;
}G[MAXN];
int head[MAXN];
struct Queue{
    int point;
    int dis;
    int kind;//0表示最短路1表示次短路
    Queue(){}
    Queue(int _point,int _dis,int _kind){ point=_point;dis=_dis; kind=_kind; }
    bool operator <(const Queue &a) const
    {
        return dis>a.dis;
    }
};

int d[MAXN][2];
int d_n[MAXN][2];
int dijkstra(int n,int m,int s,int t)//点数,边数,起点,终点
{
    memset(d,0x3f,sizeof(d));
    memset(d_n,0,sizeof(d_n));

    priority_queue<Queue> que;
    d_n[s][0]=1;d[s][0]=0;
    while(!que.empty()) que.pop();
    que.push(Queue(s,0,0));
    while(!que.empty())
    {
        Queue temp=que.top();
        que.pop();
        if(temp.dis>d[temp.point][temp.kind]) continue;
        int pn=temp.point;
        for(int i=head[pn];i!=-1;i=G[i].next)
        {
            int dis1,to1;
            dis1=temp.dis+G[i].cost;
            to1=G[i].to;
            if(dis1<d[to1][0])
            {
                if(d[to1][0]<d[to1][1])
                {
                    d[to1][1]=d[to1][0];d_n[to1][1]=d_n[to1][0];
                    que.push(Queue(to1,d[to1][1],1));
                }
                d[to1][0]=dis1;d_n[to1][0]=d_n[pn][0];
                que.push(Queue(to1,dis1,0));
            }
            else if(dis1==d[to1][0])
            {
                d_n[to1][0]+=d_n[pn][0];
            }
            else if(dis1<d[to1][1])
            {
                d[to1][1]=dis1;
                d_n[to1][1]=d_n[pn][temp.kind];
                que.push(Queue(to1,dis1,1));
            }
            else if(dis1==d[to1][1])
            {
                d_n[to1][1]+=d_n[pn][temp.kind];
            }
        }
    }
    if(d[t][0]+1==d[t][1]) return d_n[t][0]+d_n[t][1];
    else return d_n[t][0];
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        memset(head,-1,sizeof(head));
        memset(G,0,sizeof(G));
        for(int i=0;i<m;i++)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            G[i].to=b;G[i].cost=c;G[i].next=head[a];head[a]=i;
        }
        int s,t;
        scanf("%d%d",&s,&t);
        int res=dijkstra(n,m,s,t);
        printf("%d\n",res);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值