POJ 3463 Sightseeing dijkstra

本文介绍了解决求最短路径及比最短路径多1的路径数量问题的两种算法思路。第一种方法通过扩展Dijkstra算法实现,将每个节点的状态分为最短路径和次短路径两种,并使用优先队列进行更新。第二种方法则通过寻找最短路径上的点,尝试替换其边来构造次短路径。

题目链接:http://poj.org/problem?id=3463
题意:给定一个图,找最短路和比最短路多1的路的条数。
思路1:将次短路也当作一种“最短”的状态,去扩展状态。这样每个点就有两种属性,次短和最短。记录次短路条数和记录最短路条数类似。

/*
我们知道Dijkstra就是不断地用已经确定最短路的节点(黑色)去松弛未确定的点(白色),
用数学归纳法很容易证明这是正确的。它的核心思想就是某个节点的最短路一定是由它前
驱节点的最短路扩展来的。那么对于次短路也可以类似的看:一个节点的次短路一定是由
它前驱节点的最短路 or 次短路扩展而来的。那么我们就可以把节点分成两层处理:一层
处理、存储最短路,另一层处理、存储次短路。这样, 用于记录状态的数组变成了二维,
放进堆中的状态也必须是”二维”的, 这里的”二维”并不是要你开个二维数组, 而是需要在
放入堆中的结构体里多加一个标记变量, 用于标识到底是最短路还是次短路, 当然, 用于
标记已经确定最短路、次短路的点的closed表同样要变成二维的。
*/

#include<cstdio>
#include<queue>
#include<iostream>
#include<vector>
#include<map>
#include<cstring>
#include<string>
#include<set>
#include<stack>
#include<algorithm>
#define cle(a) memset(a,0,sizeof(a))
#define inf(a) memset(a,0x3f,sizeof(a))
#define ll long long
#define Rep(i,a,n) for(int i=a;i<=n;i++)
using namespace std;
const int INF = ( 2e9 ) + 2;
const ll maxn = 1e3+10;
struct edge
{
    int v,w,next;
}e[20*maxn];
struct node
{
    int u,d,flag;
    bool operator < (const node &b)const
    {
        return d>b.d;
    }
};
int tot,head[maxn],d[maxn][2],cnt[maxn][2],vis[maxn][2];
void addedge(int u,int v,int w)
{
    e[tot].v=v;
    e[tot].w=w;
    e[tot].next=head[u];
    head[u]=tot++;
}
void dijkstra(int s,int t,int n)
{
    for(int i=1;i<=n;i++)
        for(int j=0;j<2;j++)
        {
            d[i][j]=INF;
            vis[i][j]=0;
        }
    cnt[s][0]=cnt[s][1]=1;
    d[s][0]=0;
    priority_queue<node> q;
    q.push(node{s,0,0});
    while(!q.empty())
    {
        node a=q.top();q.pop();
        int u=a.u;
        if(vis[u][a.flag])continue;
        vis[u][a.flag]=1;
        for(int i=head[u];i!=-1;i=e[i].next)
        {
            int v=e[i].v,w=e[i].w;
            if(d[v][0]>a.d+w)
            {
                d[v][1]=d[v][0];
                cnt[v][1]=cnt[v][0];
                d[v][0]=a.d+w;
                cnt[v][0]=cnt[u][a.flag];
                q.push(node{v,d[v][0],0});
                q.push(node{v,d[v][1],1});
            }
            else if(d[v][0]==a.d+w)
            {
                cnt[v][0]+=cnt[u][a.flag];
            }
            else if(d[v][1]>a.d+w)
            {
                d[v][1]=a.d+w;
                cnt[v][1]=cnt[u][a.flag];
                q.push(node{v,d[v][1],1});
            }
            else if(d[v][1]==a.d+w)
            {
                cnt[v][1]+=cnt[u][a.flag];
            }
        }
    }
    if(d[t][0]+1!=d[t][1])
    printf("%d\n",cnt[t][0]);
    else
    printf("%d\n",cnt[t][0]+cnt[t][1]);
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,m;
        tot=0;
        memset(head,-1,sizeof(head));
        scanf("%d%d",&n,&m);
        for(int i=0;i<m;i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w);
        }
        int s,t;
        scanf("%d%d",&s,&t);
        dijkstra(s,t,n);
    }
}

思路2:从最短路上选择一个点,去“替换”最短路上的边,看能否凑成比最短路多1的路。

#include<cstdio>
#include<queue>
#include<iostream>
#include<vector> 
#include<map>
#include<cstring>
#include<string>
#include<set>
#include<stack>
#include<algorithm>
#define cle(a) memset(a,0,sizeof(a))
#define inf(a) memset(a,0x3f,sizeof(a))
#define ll long long
#define Rep(i,a,n) for(int i=a;i<=n;i++)
using namespace std;
const int INF = ( 2e9 ) + 2;
const ll maxn =  1e3+10;
struct edge
{
    int v,w,next;
}e[maxn*20];
struct node
{
    int u,d;
    bool operator < (const node &b)const
    {
        return d>b.d;
    }
};
int head[maxn],vis[maxn],d2[maxn],d1[maxn],cnt1[maxn],cnt2[maxn],tot;
void addedge(int u,int v,int w)
{
    e[tot].v=v;
    e[tot].w=w;
    e[tot].next=head[u];
    head[u]=tot++;

    e[tot].v=u;
    e[tot].w=w;
    e[tot].next=head[v];
    head[v]=tot++;
}
void dijkstra(int s,int n,int cnt[],int d[],int flag)
{
    for(int i=1;i<=n;i++)d[i]=INF;
    d[s]=0;
    memset(vis,0,sizeof(vis));
    priority_queue<node> q;
    q.push(node{s,0});
    cnt[s]=1;
    while(!q.empty())
    {
        node a=q.top();q.pop();
        int u=a.u;
        if(vis[u])continue;
        vis[u]=1;
        for(int i=head[u];i!=-1;i=e[i].next)
        {
            if((i&1)!=flag)continue;
            int v=e[i].v,w=e[i].w;
            if(vis[v])continue;
            if(d[v]>d[u]+w)
            {
                d[v]=d[u]+w;
                cnt[v]=cnt[u];
                q.push(node{v,d[v]});
            }
            else if(d[v]==d[u]+w)
            {
                cnt[v]+=cnt[u];
            }
        }
    }
}
int cul(int mindis,int n)
{
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        if(d1[i]+d2[i]!=mindis)
        continue;
        for(int j=head[i];j!=-1;j=e[j].next)
        {
            if((j&1)!=0)continue;
            int w=e[j].w;
            int v=e[j].v;
            if(d1[i]+d2[v]+w==mindis+1)
            ans+=cnt1[i]*cnt2[v];
        }
    }
    return ans;
} 
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,m;
        tot=0;
        memset(vis,0,sizeof(vis));
        memset(head,-1,sizeof(head));
        memset(d1,0,sizeof(d1));
        memset(d2,0,sizeof(d2));
        scanf("%d%d",&n,&m);
        for(int i=0;i<m;i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w);
        }
        int s,f;
        scanf("%d%d",&s,&f);
        dijkstra(s,n,cnt1,d1,0);
        dijkstra(f,n,cnt2,d2,1);

        printf("%d\n",cul(d1[f],n)+cnt1[f]);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值