IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 342. 道路与航线 -> 正文阅读

[数据结构与算法]342. 道路与航线

农夫约翰正在一个新的销售区域对他的牛奶销售方案进行调查。

他想把牛奶送到 T 个城镇,编号为 1~T。

这些城镇之间通过 R 条道路 (编号为 1 到 R) 和 P 条航线 (编号为 1 到 P) 连接。

每条道路 i 或者航线 i 连接城镇 Ai 到 Bi,花费为 Ci。

对于道路,0≤Ci≤10,000;然而航线的花费很神奇,花费 Ci 可能是负数(?10,000≤Ci≤10,000)。

道路是双向的,可以从 Ai 到 Bi,也可以从 Bi 到 Ai,花费都是 Ci。

然而航线与之不同,只可以从 Ai 到 Bi。

事实上,由于最近恐怖主义太嚣张,为了社会和谐,出台了一些政策:保证如果有一条航线可以从 Ai 到 Bi,那么保证不可能通过一些道路和航线从 Bi 回到 Ai。

由于约翰的奶牛世界公认十分给力,他需要运送奶牛到每一个城镇。

他想找到从发送中心城镇 S 把奶牛送到每个城镇的最便宜的方案。

输入格式

第一行包含四个整数 T,R,P,S。

接下来 R 行,每行包含三个整数(表示一个道路)Ai,Bi,Ci。

接下来 P 行,每行包含三个整数(表示一条航线)Ai,Bi,Ci。

输出格式

第 1…T 行:第 i 行输出从 S 到达城镇 i 的最小花费,如果不存在,则输出 NO PATH。

数据范围

1≤T≤25000,
1≤R,P≤50000,
1≤Ai,Bi,S≤T

输入样例:

6 3 3 4
1 2 5
3 4 5
5 6 10
3 5 -100
4 6 -100
1 3 -10

输出样例:

NO PATH
NO PATH
5
0
-95
-100

思路:

/*
这题如果直接用spfa来做的话,因为spfa已经死了,所以会被卡成 O(nm) TLE。而因为有负权边的原因,不能直接用 dij 来做。这里我们可以发现题目利用恐怖分子之名为我们提供了一个很有用的条件:单向边没有回路。如果通过一条单向边从 a 走到 b ,那么必不可能通过另外一条单向边从 b 走到 a 。因此这里我们可以把单向边连起来的每一块看成是一个连通块。因为联通块内部是没有负权边的,因此在联通块内部可以使用 dij 来求得最短路。而在联通块之间虽然有负权边,但这些负权边组成了一个 DAG (有向无环图)。因此我们可以通过求拓扑序列的方式来求得其最短路。时间复杂度是 O(n)。这样我们就能把总体的时间复杂度控制在 O(nlogm) 了。

1、先输入所有双向道路,然后dfs出所有连通块,计算两个数组:id[]存储每个点属于哪个连通块;
   vector<int>block[]存储每个连通块里有哪些点
2、输入所有航线,同时统计出每个连通块的入度
3、按照拓扑序,依次处理每个连通块,先将所有入度为0的连通块的编号加入队列中。
4、每次从队头取出一个连通块的编号。
5、将block[bid]中的所有点加入堆中,然后对堆中所有点做dijkstra。
6、每次取出堆中的距离最小的点ver。
7、然后遍历ver的所有邻点j,如果id[ver] == id[j],如果j能被更新,则将j插入堆中;
   否则说明访问到一条航线,则将id[j]这个连通块的入度减一,如果减到0了,就将其插入到拓扑序的队列中
*/

代码:

#include <bits/stdc++.h>
#define x first;
#define y second;
using namespace std;
typedef pair<int, int> PII;
const int N = 25010, M = 150010, INF = 0x3f3f3f3f;
int n, mr, mp, S;
int id[N];
int h[N], ne[M], e[M], w[M], idx;
int dist[N], din[N];
vector<int> block[N];
int bcnt;
queue<int> qu;
int st[N];

void add(int a, int b, int c)
{
    e[idx] = b;
    w[idx] = c;
    ne[idx] = h[a];
    h[a] = idx++;
}

void dfs(int u, int bid) // 遍历所有块
{
    id[u] = bid, block[bid].push_back(u);
    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (!id[j])
            dfs(j, bid);
    }
}

void dijkstra(int bid)
{
    priority_queue<PII, vector<PII>, greater<PII>> heap;
    for (auto it : block[bid]) // 全都要放入 不知道哪个最小
        heap.push({dist[it], it});

    while (heap.size())
    {
        PII t = heap.top();
        heap.pop();

        int ver = t.y;
        if (st[ver])
            continue;
        st[ver] = 1;

        for (int i = h[ver]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (id[j] != id[ver] && --din[id[j]] == 0)
                qu.push(id[j]);
            if (dist[j] > dist[ver] + w[i])
            {
                dist[j] = dist[ver] + w[i]; // 负权边也更新
                if (id[j] == id[ver])
                    heap.push({dist[j], j});
            }
        }
    }
}

void topsort()
{
    memset(dist, 0x3f, sizeof(dist));
    dist[S] = 0;

    for (int i = 1; i <= bcnt; i++)
    {
        if (!din[i])
            qu.push(i);
    }

    while (qu.size())
    {
        int t = qu.front();
        qu.pop();
        dijkstra(t);
    }
}

int main()
{
    cin >> n >> mr >> mp >> S;
    memset(h, -1, sizeof(h));
    while (mr--)
    {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c), add(b, a, c);
    }

    for (int i = 1; i <= n; i++)
    {
        if (!id[i])
        {
            bcnt++;
            dfs(i, bcnt);
        }
    }

    while (mp--)
    {
        int a, b, c;
        cin >> a >> b >> c;
        din[id[b]]++;
        add(a, b, c);
    }

    topsort();

    /*
     对于这题来说,假设起点在第三个连通块里,第一个连通块到第二个连通块有一条负权的航线,
    那么第二个连通块中的dist会被更新为dist[i] < INF。根据题意不存在环,因此第一个和第二个连通块中的城镇都是不可达的
    */
    for (int i = 1; i <= n; i++)
        if (dist[i] > INF / 2)
            cout << "NO PATH" << endl;
        else
            cout << dist[i] << endl;

    return 0;
}
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-04-18 18:07:50  更:2022-04-18 18:09:58 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 7:43:17-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码