POJ-1511 Invitation Cards (SPFA算法+邻接表)

8 篇文章 0 订阅

题目大意:

这道题和我以前做过的一道题很像,大概就是说,求得源点到各个点的最短路径相加,和各个点到源点的最短路径相加之和的和。源点到各点的最短路径其实很好求,各点到源点的最短路径其实就是将矩阵翻一下,然后再求源点到各点的最短路径。

如果是普通的数据量的话,直接用dijkstra算法做就好了,效率也不低。但是这道题的数据量太大了,dijkstra算法的缺点就是空间复杂度太大(n^2),所以需要用到邻接表。



算法分析:

SPFA算法我不做过多讨论,因为其实很好理解,理解不了直接背模板也很容易。关键是邻接表的实现,我做这道题之前没有接触过邻接表,所以先去找邻接表的实现的博客看,结果看得一头雾水,最后靠看别人的源码一点一点琢磨出来的。其实邻接表没有什么特殊算法,就是一些编程的小技巧。
先定义一个数组: int front[MAXNUM];
这个数组的作用是保存当前节点的第一个下一节点的位置(即表头节点),所以一开始赋初值为0(0意味着当前节点不指向任何节点)。
定义一个结构体数组:
struct {
int to, cost, next; // to是节点位置,cost是边的权值,next是指向一下一节点
} edge[MAXNUM];

代码实现:

// crazy_mad写于2016.10.23,POJ-1511题,使用spfa+邻接表成功做出

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <iostream>
#include <algorithm>
#include <queue>

#define MAXNUM 1LL<<32

using namespace std;

struct node
{
    int to;
    int cost;
    int next;
} edge1[1000010], edge2[1000010];

int n, m;
int front_1[1000010];
int front_2[1000010];
long long minn[1000100];
bool statu[1000010];

void insert_1(int index, int from, int to, int cost)
{
    edge1[index].to = to;
    edge1[index].cost = cost;
    edge1[index].next = front_1[from];
    front_1[from] = index;
}

void insert_2(int index, int from, int to, int cost)
{
    edge2[index].to = to;
    edge2[index].cost = cost;
    edge2[index].next = front_2[from];
    front_2[from] = index;
}

long long spfa_1()
{
    long long sum = 0;
    int index, from, to;

    memset(statu, 0, sizeof(statu));
    for (int i = 1; i <= n; i++) {
        minn[i] = MAXNUM;
    }
    queue<int> queue_;
    queue_.push(1);
    minn[1] = 0;
    for (int i = 1; i <= n; i++) {
        from = queue_.front();
        queue_.pop();
        index = front_1[from];
        statu[from] = false;            // 重置状态
        while (index) {
            to = edge1[index].to;
            if (minn[to] > minn[from] + edge1[index].cost) {
                minn[to] = minn[from] + edge1[index].cost;
                if (!statu[to]) {
                    queue_.push(to);
                    statu[to] = true;
                }
            }
            index = edge1[index].next;
        }
    }
    for (int i = 1; i <= n; i++) {
        sum += minn[i];
    }

    return sum;
}

long long spfa_2()
{
    long long sum = 0;
    int index, from, to;

    memset(statu, 0, sizeof(statu));
    for (int i = 1; i <= n; i++) {
        minn[i] = MAXNUM;
    }
    queue<int> queue_;
    queue_.push(1);
    minn[1] = 0;
    for (int i = 1; i <= n; i++) {
        from = queue_.front();
        queue_.pop();
        index = front_2[from];
        statu[from] = false;            // 重置状态
        while (index) {
            to = edge2[index].to;
            if (minn[to] > minn[from] + edge2[index].cost) {
                minn[to] = minn[from] + edge2[index].cost;
                if (!statu[to]) {
                    queue_.push(to);
                    statu[to] = true;
                }
            }
            index = edge2[index].next;
        }
    }
    for (int i = 1; i <= n; i++) {
        sum += minn[i];
    }

    return sum;
}

int main()
{
    int t;
    int x, y, z;

    scanf("%d", &t);
    while (t--) {
        scanf("%d%d", &n, &m);
        memset(front_1, 0, sizeof(front_1));
        memset(front_2, 0, sizeof(front_2));
        for (int i = 1; i <= m; i++) {
            scanf("%d%d%d", &x, &y, &z);
            insert_1(i, x, y, z);
            insert_2(i, y, x, z);
        }
        cout << spfa_1() + spfa_2() << endl;
    }

    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值