HDU 4725 The Shortest Path in Nya Graph (最短路djs+优先队列优化)

时间:2023-02-02 20:54:38

【题目链接】
http://poj.org/problem?id=4725

题目意思

题意说有n个节点,这些节点分布在n个平面上,两两相邻的平面直接的距离为c。而节点与节点也有m条边,距离为w。问你从节点1到n最短路。

解题思路

题意看了半天,才明白节点与面的关系。一开始觉的直接把面也当成节点来构图就可以了(面与面里的节点距离为0)。但是后来发现题目并没有说一个面就一个节点,当一个面出现多个节点时候,这些点的距离全部变成0了,这不符合题意。然后看大佬们怎么构图的。发现两种:一种还是把面当成一个节点,但是不在连面内部的节点与面的边,而直接连相邻面的节点距离为c,这样避免同面为0的情况(觉的好像不好写,没深究不知道有没有理解错)。而第二种就把一个面分为两个点,一个入面点,一个出面点。而把双向边变成单向边。(构完图最后跑遍单源最短路就可以了,但是要用优先队列优化不然会T)
总结下第二种构图方法注意点:
1.节点与对应层的入点有条单向边。(入指向节点)
2。节点与对应层的出点有条单向边。(出指向节点)
3.相邻两层都有i出指向出(i+1入,(i+1)出指向i入的两条单向边。(没节点的层并不影响结果)
4.m条节点到节点的双向边。

代码部分


#include <iostream>
#include <algorithm>
#include <stdio.h>
#include <string.h>
#include <queue>
#include <string>
#include <map>
using namespace std;
#define LL long long
#define inf 0x3f3f3f3
const int N = 1e5+5;
int n,m,c;
int dis[3*N];
int vis[3*N]; 
struct node 
{
    int i,c;   ///节点编号和价值 
    friend bool operator < (node a,node b)  
    {
        return a.c>b.c;
    }
};
vector <node> M[3*N];  ///i~n为节点,n+1到2n为每层的出口,2n+1到3n为每层的出口 
node add (int i,int c) ///转换成node类型 
{
    node t;
    t.i = i;
    t.c = c;
    return t;
}
void init()  ///初始化 
{
    for (int i = 1; i <= 3*n; i++)
    {
        M[i].clear();
        dis[i] = inf;
        vis[i] = 0;
    }
    for (int i = 1; i < n; i++) 
    {
        M[2*n+i+1].push_back(add(n+i,c));  ///上一层出边到这层入边 
        M[n*2+i].push_back(add(n+i+1,c));  ///这层出边 到上一层入边 
    }
}
void Dijkstra()
{
    priority_queue<node>q;
    q.push(add(1,0));
    dis[1] = 0;
    while (!q.empty())
    { 
        node t = q.top();
        q.pop();
        if (vis[t.i])  ///节点t.i松弛过 
            continue;
        vis[t.i] = 1;
        for (int i = 0; i < M[t.i].size();i++)
        {
            node k = M[t.i][i];
            if (dis[k.i] > dis[t.i]+k.c && !vis[k.i])  ///松弛 
            {
                dis[k.i] = dis[t.i ]+k.c;
                q.push(add(k.i,dis[k.i]));
            }
        }
    }
}
int main()
{
    int T,cas = 1;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d %d %d",&n,&m,&c);
        init();
        for (int i = 1; i <= n; i++)
        {
            int k;
            scanf("%d",&k);
            M[n+k].push_back(add(i,0));   ///层入边到节点
            M[i].push_back(add(2*n+k,0));   ///节点到层出边 
        }
        for (int i = 0; i < m; i++)
        {
            int u,v,w;
            scanf("%d %d %d",&u,&v,&w);
            M[u].push_back(add(v,w));  ///节点与节点的边 
            M[v].push_back(add(u,w));
        } 
        Dijkstra();
        if (dis[n] == inf)
            dis[n] = -1;
        printf("Case #%d: %d\n",cas++,dis[n]);
    }
    return 0;
}