HDU 4725 层级最短路 思维建图 邻接表 堆优化迪杰斯特拉 真的难想 区域网络赛真题

43 篇文章 1 订阅

题目

在这里插入图片描述

题解思路

知道是最短路,怎么建图呢?
一开始想到每层来一个超级源点,但是方向不知道怎么确定,用双向边果然WA了(如果是双向边,那一层的都会变成0费连通了 )。
翻了翻 大佬的博客
大佬定义了一种规则,让这个超级源点实现了功能

对于上下层的源点,进行 点 到 源点 单向连通
对于本层源点进行 源点 到 点单向连通
思考一下 如果反过来 也是可行的

属实巧妙啊!
然后就是跑最短路了,这里是10的5次方,朴素的迪杰斯特拉肯定会TLE,刚好没用过堆优化和邻接表,试试水!
堆优化 记得重载运算符 ( 这个运算符是优先队列用的 )

struct bian
{
    int z,w;
    bool operator < (const bian &other) const
    {
        return w > other.w;
    }
};

AC代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <queue>
using namespace std;
#define INF 0x3f3f3f3f
int li[200020];
bool vis[200020];
struct bian
{
    int z,w;
    bool operator < (const bian &other) const
    {
        return w > other.w;
    }
};
vector <bian> head[200020];
int dis[200020] ;
int fb ( int x )
{
    if (x < 0 )
        return -x;
    return x;
}
int main ()
{
    ios::sync_with_stdio(false);
    int t,sum = 1;
    cin>>t;
    while(t--)
    {
        int n,m,c,ceshu = 0;
        bian tmm ;
        cin>>n>>m>>c;
        for ( int i = 1 ;i <= n ; i++)
        {
            int ti;
            cin>>ti;
            ceshu = max(ceshu,ti);  //取出层数
            li [i] = ti ;

        }
        for (int i = n+1 ; i<= n+ceshu ; i++)
            li[i] = i - n;  //建立超级源点
        for (int i = 1 ;i <= n ; i++ )
        {
            tmm.z =n+li[i] ;
            tmm.w = 0 ;
            head[ i ].push_back (tmm);  //建立和超级源点的单向边
        }
        for (int i = 1 ; i<= n ; i++)
        {
            tmm.w = c;
            if ( li[i] >  1 )
            {
                tmm.z =  i ;
                head[ n+li[i]-1 ].push_back(tmm);  //向和下方的超级源点连接
            }
            if ( li[i] < ceshu)
             {
                  tmm.z = i ;
                  head[ n+li[i]+1 ].push_back(tmm); //向和上方的超级源点连接
             }
        }
        for (int i = 1 ;i <= m ; i++ )
        {
            int a,b,wahs;
            cin>>a>>b>>wahs;
            tmm.z = b;
            tmm.w = wahs;
            head[a].push_back(tmm);
            tmm.z = a;
            head[b].push_back(tmm);  //建立原本的额外边
        }
     /*   for (int i = 1 ;i <= ceshu+n ; i++ )
        {
            printf("以%d为首的边\n",i);
            int pint = head[i].size();
            for (int k = 0 ;k < pint ; k++ )
                printf("%d %d %d\n",i,head[i][k].z,head[i][k].w );
            printf("\n");
        }  */
        for (int i = 1 ; i <= n+ceshu ; i++ )
            dis[ i ] = INF ;  //初始化dis数组
        memset(vis,0,sizeof(vis));
        dis [ 1 ] = 0;  //以1为起点出发
        priority_queue < bian > q;
        tmm.w = 0;
        tmm.z = 1;
        q.push(tmm);  //将起点放入优先队列
        while(!q.empty())
        {
            tmm = q.top();
            q.pop();            //取出和源点最近的点并出队
            int go = tmm.z;
            if (vis[go])
                    continue;
            vis[go] = 1;
            for (int i = 0 ; i <head[go].size() ; i++)
            {
                tmm = head[go][i];    //对该源点有的边 进行松弛判断
                if (dis[tmm.z] > dis[go] + tmm.w )
                {
                    dis[tmm.z] = dis[go] + tmm.w;
                    bian e;
                    e.w = dis[tmm.z];
                    e.z = tmm.z;
                    q.push(e);  //将 已经被松弛的点 和 当前松弛该点到起点的最小距离 入队
                }
            }
        }
        cout<<"Case #"<<sum<<": ";
        sum++;
        if (dis[n] == INF)
            cout<<"-1\n";
        else
            cout<<dis[n]<<"\n";
        for (int i = 1 ;i <= n + ceshu ; i++ )  //每次用完初始化
            head[i].clear();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值