首页 > 其他分享 >天梯赛-练习集 -L2-001 紧急救援, 一个不是很正常的过题方法

天梯赛-练习集 -L2-001 紧急救援, 一个不是很正常的过题方法

时间:2024-04-11 12:22:36浏览次数:15  
标签:cnt ll head 过题 001 dijkstra L2 maxn dis

L2-001 紧急救援

代码长度限制:16 KB
时间限制:200 ms
内存限制:64 MB

题目描述

作为一个城市的应急救援队伍的负责人,你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候,你的任务是带领你的救援队尽快赶往事发地,同时,一路上召集尽可能多的救援队。

输入格式

输入第一行给出4个正整数NMSD,其中N(2≤N≤500)是城市的个数,顺便假设城市的编号为0 ~ (N−1)M是快速道路的条数;S是出发地的城市编号;D是目的地的城市编号。第二行给出N个正整数,其中第i个数是第i个城市的救援队的数目,数字间以空格分隔。随后的M行中,每行给出一条快速道路的信息,分别是:城市1、城市2、快速道路的长度,中间用空格分开,数字均为整数且不超过500。输入保证救援可行且最优解唯一

输出格式

第一行输出最短路径的条数和能够召集的最多的救援队数量。第二行输出从SD的路径中经过的城市编号。数字间以空格分隔,输出结尾不能有多余空格

输入样例:

4 5 0 3
20 30 40 10
0 1 1
1 3 2
0 3 3
0 2 2
2 3 2

输出样例

2 60
0 1 3

笔者观点

  1. 显然这是一条最短路问题,而且只需求一条最短路(起点和终点已定),要求的东西也不算很多,所以这是一道dijkstra的板子题啊。(没学过dijkstra的小伙伴可以看洛谷的 单源最短路径
  2. 虽然这道题的时间很短,但是城市数模非常少,不到500,dijkstra在数据量很大的时候也才跑50ms,所以这道题我们的时间是挺充裕的。
  3. 没有具体给出M的大小,由城市个数我们大概可以猜测M的大小并不会很小,我们需要一个很的的容器去存M(笔者最开始因为这个问题RE了QAQ)
  4. 很坑的一点就是你要意识到这里给出的路径都是双向路,并不是单向的。
  5. 输出结尾不能有多余的空格(天梯的题就是这么矫情

具体的思路

显然这道题是有两个权重的,一个是普通的路段的长度,另一个是城市中的人数。由于需要同时求出最短路的数量和最短而且召集人数最多的路,笔者愚钝,首先想到的只是跑两边dijkstra?一遍求出最短路的条数,第二边考虑上城市人数求出最优解。结果表明这样做是可以的,并不会超时,当然你完全也可以只跑一遍就求出答案。

关键的地方

使用from来存储到达该点的最短路的条数,使用father来储存到达该点的最短路
的上一个节点,prize来保存每个城市的人数,head,vis,dis为dijkstra的必备数组。

const ll INF = 9223372036854775800;
ll cnt = 0;
ll n, m, s, goal;
ll head[maxn], vis[maxn], dis[maxn];
ll from[maxn];
ll prize[maxn];
ll father[maxn];

因为是双向的(无向)的路,所以我们每次要记录两条

void add1(ll u, ll v, ll w)
{
    e[++cnt].u = u;    //第一次
    e[cnt].v = v;
    e[cnt].w = w;
    e[cnt].next = head[u];
    head[u] = cnt;

    e[++cnt].u = v;   //第二次
    e[cnt].v = u;
    e[cnt].w = w;
    e[cnt].next = head[v];
    head[v] = cnt;
}

在第二次dijkstra中我们需要同时考虑两个权重———路的长度和城市的人的数量,我们可以写一个比较函数,在路的长度不一样的时候选择路更短的那一条路,在路的长度一样的时候选择人数较多的那一条路。另外也可以用我的这种做法,将每一条路的权重设置为路的 长度x100000000-路的重点的人口的数量。这样得到的新的权重最小的便是路长度最小并且得到的人数最多的那条路。

void add2()
{
    for (int i = 0; i <= cnt; i++)
    {
        e[i].w = e[i].w * 100000000 - prize[e[i].u];   //改变权重
    }
    for (int i = 0; i <= n; i++)
    {
        vis[i] = 0;     //初始化
    }
}

后面的dijkstra算法就简单很多了,这里只贴出关键部分

for (ll i = head[u]; i; i = e[i].next)
        {
            ll v = e[i].v;
            if (dis[v] > dis[u] + e[i].w)
            {
                dis[v] = dis[u] + e[i].w;
                q.push((node){dis[v], v});
                from[v] = from[u];    //到达某点的路径数量,如果路径小,则直接赋值
                father[v] = u;    //记录父节点
            }
            else if (dis[v] == dis[u] + e[i].w)
            {
                dis[v] = dis[u] + e[i].w;
                q.push((node){dis[v], v});
                from[v] += from[u];   //到达某点的路径数量,如果路径相等,则路径数量相加
                father[v] = u;   //记录父节点
            }
        }

完整代码

点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1000;
const ll INF = 9223372036854775800;
ll cnt = 0;
ll n, m, s, goal;
ll head[maxn], vis[maxn], dis[maxn];
ll from[maxn];
ll prize[maxn];
ll father[maxn];

struct edge
{
    ll v, u, w, next;
} e[10000007];

struct node
{
    ll w;
    ll now;
    inline bool operator<(const node &a) const  //比较方法
    {
        return w > a.w;
    }
};
priority_queue<node> q;   //大根堆

void add1(ll u, ll v, ll w)
{
    e[++cnt].u = u;
    e[cnt].v = v;
    e[cnt].w = w;
    e[cnt].next = head[u];
    head[u] = cnt;

    e[++cnt].u = v;
    e[cnt].v = u;
    e[cnt].w = w;
    e[cnt].next = head[v];
    head[v] = cnt;
}

void add2()
{
    for (int i = 0; i <= cnt; i++)
    {
        e[i].w = e[i].w * 100000000 - prize[e[i].u];
    }
    for (int i = 0; i <= n; i++)
    {
        vis[i] = 0;
    }
}

void djtestua()
{
    for (int i = 0; i < n; i++)
    {
        father[i] = -1;
        dis[i] = INF;
        from[i] = 1;
    }
    dis[s] = 0;
    q.push((node){0, s});
    while (!q.empty())
    {
        node temp = q.top();
        ll u = temp.now;
        q.pop();
        if (vis[u])
            continue;
        vis[u] = 1;
        for (ll i = head[u]; i; i = e[i].next)
        {
            ll v = e[i].v;
            if (dis[v] > dis[u] + e[i].w)
            {
                dis[v] = dis[u] + e[i].w;
                q.push((node){dis[v], v});
                from[v] = from[u];
                father[v] = u;
            }
            else if (dis[v] == dis[u] + e[i].w)
            {
                dis[v] = dis[u] + e[i].w;
                q.push((node){dis[v], v});
                from[v] += from[u];
                father[v] = u;
            }
        }
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin >> n >> m >> s >> goal;

    for (int i = 0; i < n; i++)
    {
        cin >> prize[i];
    }

    for (int i = 0; i < m; i++)
    {
        ll a, b, c;
        cin >> a >> b >> c;
        add1(a, b, c);
    }
    djtestua();
    // cout << "num: ";
    cout << from[goal] << " ";
    // cout << endl;
    // cout << "from " << endl;
    // for (int i = 0; i < 10; i++)
    // {
    //     cout << from[i] << ' '; 
    // }
    // cout << endl;

    add2();
    djtestua();

    // cout << "father  "  << endl;;
    // for (int i = 0; i < 10; i++)
    // {
    //     cout << father[i] << ' ';
    // }
    // cout << endl;

    ll sum = 0;
    stack<ll> sta;
    ll goaltemp = goal;

    while (goaltemp != -1)
    {
        sta.push(goaltemp);
        sum += prize[goaltemp];
        goaltemp = father[goaltemp];
    }

    // cout << "sum : ";
    cout << sum;
    cout << endl;

    //cout << "dian: " << endl;
    cout << sta.top();
    sta.pop();
    while (!sta.empty())
    {
        cout << " ";
        cout << sta.top();
        sta.pop();
    }

    return 0;
}

结果

虽然跑了两次dijkstra,但是还是在最慢48ms跑出了答案,还是可以的,这么做的好处我觉得有两个,一是比较好想,还是直接套的dijkstra的板子,只是我们把路径的权重进行的处理。二是代码比较好写,只需重写一个算权重的函数就可以直接再跑一次dijkstra了。

标签:cnt,ll,head,过题,001,dijkstra,L2,maxn,dis
From: https://www.cnblogs.com/acidbarium/p/18127991

相关文章

  • java代码将16进制字符串转换为图片,jdbc入库blob字段,解决ORA-01704,PLS-00172,ORA-06550,
    从Oracle导出SQL文件中的insert语句包含blob字段,语句HEXTORAW函数将16进制的字符串入库,由于字符串太长,insert失败下面的代码读取完整的insert语句,将HEXTORAW函数连同16进制的字符串替换为NULL,先将字段置空插入记录,然后使用PreparedStatement对图片文件读流更新入库importorg.......
  • (Java)数据结构——排序(第一节)堆排序+PTA L2-012 关于堆的判断
    前言本博客是博主用于复习数据结构以及算法的博客,如果疏忽出现错误,还望各位指正。堆排序(HeapSort)概念堆排序是一种基于堆数据结构的排序算法,其核心思想是将待排序的序列构建成一个最大堆(或最小堆),然后将堆顶元素与最后一个元素交换,再将剩余元素重新调整为最大堆(或最小堆),重复......
  • MHY1001. [崩三]椰子树题解
    #include<bits/stdc++.h>usingnamespacestd;constintN=1010,M=20010;intq[M];//s的最大值为20000,v的最小值为1,所以队列里面最多是会有200010个元素的intn,m;intf[M],g[M];intmain(){cin>>n>>m;for(inti=1;i<=n;++i){......
  • JAVA L2-041 插松枝
    【22年真题】这是一道并不完美的题解,还有很多纰漏但已经是我的极限了...记录一下importjava.io.StreamTokenizer;importjava.util.ArrayDeque;importjava.util.Deque;importjava.util.Iterator;importjava.io.InputStreamReader;importjava.io.BufferedReader;i......
  • 0001命令式和声明式
    命令式命令式框架的一大特点就是关注过程就如下面的代码,自然语言描述能够与代码产生一一对应的关系,代码本身描述的是"做事的过程",这符合我们的逻辑直觉constdiv=document.querySelector('#app')//获取divdiv.innerText='helloworld'/......
  • NL2SQL基础系列(1):业界顶尖排行榜、权威测评数据集及LLM大模型(Spider vs BIRD)全面对比
    NL2SQL基础系列(1):业界顶尖排行榜、权威测评数据集及LLM大模型(SpidervsBIRD)全面对比优劣分析[Text2SQL、Text2DSL]Text-to-SQL(或者Text2SQL),顾名思义就是把文本转化为SQL语言,更学术一点的定义是:把数据库领域下的自然语言(NaturalLanguage,NL)问题,转化为在关系型数据库中可以执行的......
  • WSL2-Ubuntu Pytorch深度学习开发环境搭建
    安装Linux发行版删除现有Linux发行版wsl-l-vwsl--unregisterUbuntu从MicrosoftStore安装Linux发行版设置用户名和密码安装CUDACUDA(ComputeUnifiedDeviceArchitecture)是由NVIDIA推出的并行计算平台和编程模型。CUDAToolkit是由NVIDIA提供的一套用于GPU开发......
  • 在Linux中,如何配置和使用fail2ban来防止暴力攻击?
    fail2ban是一个用于防止暴力攻击(如破解密码尝试)的安全工具,它通过监控系统日志文件来检测异常行为,并在检测到多次失败的登录尝试后,自动采取措施(如暂时或永久地阻止攻击者的IP地址)。1.配置fail2ban安装fail2ban:使用你的Linux发行版的包管理器安装fail2ban。例如,在基于Debian的......
  • Python学习笔记-001
    记录一下重学python,虽然python老早就接触了,更多的还是停留在语法层面,老c++了。最近打算从头开始系统拉一下python的基础,先预计8天学完。后面还打算系统学一下Numpy,Pandas和Matplotlib。python基础教程python简介检查是否安装python,终端输入python--versionpython是一种解释......
  • 001-洞中兽
    洞中兽TheBeastintheCave背景此短篇小说的初稿是洛夫克拉夫特大约于1904年春天之前写成的,终稿于1905年4月21日完成。由于作者本人并没有洞穴居住的亲身经历,所以他花了几天的时间待在普罗维登斯公共图书馆里,潜心研究本篇小说的发生地——位于美国肯塔基州中部的猛犸洞国......