poj1511+链式前向星+dijkstra堆优化

如果看题解往下划拉……
这题网上说卡数据,只能链式前向星才能过?正好学一下链式前向星的处理
其实:这个和vector做的邻接表差不多。可能用起来会快一点?(不然这题邻接表为啥过不了)
我觉得dijkstra的堆优化和spfa算有非常强的关联性
这里提几句:(分析一下异同)

spfa是一种基于bfs的单源最短路算法,起源于bellman-ford。
这个算法名是个梗,一个西南交大的哥们自称发现了这个算法,毕业论文写的这个。他的神之分析复杂度为(m)……
对,就是边的个数。其实这个算法的复杂度是(n*m).
算法实质就是:bfs遍历源到每个点,如果你熟悉二维迷宫问题的话,也就是每到一个点,就距离+1.其实思路是一样,只不过,因为在图中的bfs因为边权并不是单调递增的,(不是每跳边均为1),就是存在了一个更优j点,让你原本从i到o的距离,因为j点的存在,缩小了。但o之前已经访问过了,到o的距离应该是已经确定,所以o就不能再次被访问了。但实际上从i到j到o这样才是最优。如何给这个bfs机制一个反悔的过程?那就是把vis去掉!但是去掉这个数组你会发现,bfs会一直循环……所以还是需要vis标记。那怎么办?好办!因为bfs的过程需要一个队列,当o在队列中的时候,我们就标记vis,一出队列,就把他的标记取消,这样,o可以重复访问,

**SPFA思想:**是一种暴力,因为每次的重复访问都是找到了更优的方案,也就是意味着,当方案全都最优的时候,队列里面就啥也没有了。


dijkstra堆优化算法呢?
也是bfs的思路开始,从源点遍历周围的每个点,但是这个算法借住了堆序列,保证每次的出队列操&遍历作都是从源点到的距离最小的点开始,也就意味着,这个过程是一个新加入的边权单调递增的过程。实际也是一种起源bfs的算法!但是做起来就变成了dfs……简易自习揣摩迷宫问题,与这个最短路问题的联系。其实思想同源,很多人说这个算法是一种贪心,其实这个贪心思维我觉得应该是dijkstra,而不是dijkstra堆优化。dijkstra堆优化理解上更易,普通问题可以通过vector邻接表过,而且很块
卡点的可能需要链式前向星。
这个题就卡这了吧
学习这个小用法的视频
https://www.bilibili.com/video/BV1mJ411S7BB?from=search&seid=479824852082650829
AC代码,鄙人不才,代码可能不是那么pl

#include <iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<vector>
#include<stack>
using namespace std;
typedef long long ll;
const int M=1000005;
int p,q;

struct node
{
    int to,next;
    ll w;
} edg[M*2];
int tot;
int head[M];
int rhead[M];

void addedge(int u,int v,long long w)
{
    edg[tot].to=v;
    edg[tot].w=w;
    edg[tot].next=head[u];
    head[u]=tot++;
}

void rad(int u,int v,long long w)
{
    edg[tot].to=v;
    edg[tot].w=w;
    edg[tot].next=rhead[u];
    rhead[u]=tot++;
}

ll dis[M];
ll rdis[M];

int vis[M];
int choice=1;
struct cmp
{
    bool operator()(int a, int b)
    {
        if(choice==1)	//代表正向dij的比较
            return dis[a] > dis[b];	//这比较函数,涨见识,可以用到全局变量
        return rdis[a] > rdis[b];
    }
};

ll dijk(int s,int head[],ll dis[])
{

    priority_queue<int,vector<ll>,cmp> que;
    que.push(s);
    memset(vis,0,sizeof(vis));
    dis[s]=0;
    while(!que.empty())
    {
        int now=que.top();
        que.pop();
        vis[now]=1;
        for(int i=head[now]; i!=0; i=edg[i].next)
        {
            if(dis[edg[i].to]>dis[now]+edg[i].w)
            {
                dis[edg[i].to]=dis[now]+edg[i].w;
                if(!vis[edg[i].to])
                    que.push(edg[i].to);
            }
        }
    }
    ll ans=0;
    for(int i=1; i<=p; i++)
    {
        ans+=dis[i];
    }
    return ans;

}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&p,&q);
        int a,b;
        ll c;
        tot=1;
        memset(head,0,sizeof(head));
        memset(rhead,0,sizeof(rhead));
        while(q--)
        {
            scanf("%d%d%lld",&a,&b,&c);
            addedge(a,b,c);
            rad(b,a,c);
        }

        ll ans=0;
        choice=1;
        memset(dis,0x3f3f3f3f,sizeof(dis));
        ans+=dijk(1,head,dis);
        choice=0;
        memset(rdis,0x3f3f3f3f,sizeof(rdis));
        ans+=dijk(1,rhead,rdis);
        printf("%lld\n",ans);



    }


    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值