P4568 [JLOI2011] 飞行路线(洛谷)(dijkstra优化+动规)

题目描述

Alice 和 Bob 现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在 n 个城市设有业务,设这些城市分别标记为 0 到 n−1,一共有 m 种航线,每种航线连接两个城市,并且航线有一定的价格。

Alice 和 Bob 现在要从一个城市沿着航线到达另一个城市,途中可以进行转机。航空公司对他们这次旅行也推出优惠,他们可以免费在最多 k 种航线上搭乘飞机。那么 Alice 和 Bob 这次出行最少花费多少?

输入格式

第一行三个整数 n,m,k,分别表示城市数,航线数和免费乘坐次数。

接下来一行两个整数 s,t,分别表示他们出行的起点城市编号和终点城市编号。

接下来 m 行,每行三个整数 a,b,c,表示存在一种航线,能从城市 a 到达城市 b,或从城市 b 到达城市 a,价格为 c。

输出格式

输出一行一个整数,为最少花费。

思路:dijkstra优化,动态规划;

核心:假设 i 代表当前节点,j 是免费次数,i - 1 是指当前节点的上一个节点(父节点),w是这两个节点间的价格,dis是到达i节点,用了j次的免费次数所用的最小价格。

        // dis(i,j) =  min( dis(i-1,j-1), dis(i-1,j)+ w )

就只需要套dijkstra优化的模板,在求最短路时加入上一段的动规来更新数据即可。

代码如下:

#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 5;
typedef long long ll;

int n, m, k;
int s, t;
int T;

// 建图
struct node
{
    int e, w;
};
vector<node> v[N];

// dijkstra优先队列
struct Node
{
    int x, y, cnt; // 分别代表:价格  当前节点  免费的次数
    friend bool operator<(Node aa, Node bb)
    {
        return aa.x > bb.x; // 结构体中,x小的优先级高,即x最小的在最上面
    }
};
priority_queue<Node> q;

int dis[N][12]; // 记录最短距离
bool st[N][12]; // 记录是否被遍历过

void dijkstra()
{
    dis[s][0] = 0;
    q.push({0, s, 0});

    while (q.size())
    {
        Node h = q.top();
        q.pop();
        int h1 = h.x, h2 = h.y, h3 = h.cnt; // 价格 当前节点 免费次数

        if (st[h2][h3]) continue;
        st[h2][h3] = true;
        // 假设i代表当前节点,j是免费次数,i-1是指当前节点的上一个节点(父节点)(注:注释i,j与下方代码i,j不同,只是方便陈述注释)
        // dis(i,j) =  min( dis(i-1,j-1), dis(i-1,j)+ w )
        for (int i = 0; i < v[h2].size(); i++)
        {
            int z1 = v[h2][i].e, z2 = v[h2][i].w; // z1:下一个节点  z2:从当前节点(h2)到下一个节点(z1)的价格

            if (h3 < k && dis[z1][h3 + 1] > dis[h2][h3]) // 如果还有免费次数,且价格更低,更新数据
            {
                dis[z1][h3 + 1] = dis[h2][h3];
                q.push({dis[z1][h3 + 1], z1, h3 + 1});
            }

            if (dis[z1][h3] > dis[h2][h3] + z2) // 只要价格更低,直接更新当前数据
            {
                dis[z1][h3] = dis[h2][h3] + z2;
                q.push({dis[z1][h3], z1, h3});
            }
        }
    }
}

void solve()
{
    memset(dis, 0x3f, sizeof dis); // 初始化
    cin >> n >> m >> k;
    cin >> s >> t;
    while (m--)
    {
        int a, b, c;
        cin >> a >> b >> c;
        v[a].push_back({b, c});
        v[b].push_back({a, c});
    }

    dijkstra();

    // 把终点的全部情况遍历一遍
    int ans = 0x3f3f3f3f;
    for (int i = 0; i <= k; i++)
    {
        ans = min(ans, dis[t][i]);
    }

    cout << ans;
}

int main()
{
    ios::sync_with_stdio(false), cin.tie(0); // 关闭同步流,可以让输入更快,弊端自行csdn
    T = 1;
    while (T--)
    {
        solve();
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值