P1875 佳佳的魔法药水

P1875 佳佳的魔法药水

题意

现在一共有n个点,每个点都有一个代价,接下来告诉你多组a、b、c,表示,通过a、b可以到达c,可能会存在自环,现在问你到达一号点的最小的代价,并且这个代价有多少条方案。

思路

在这里插入图片描述
流汗黄豆。。。。。
Dijkstra
我们现在知道没个点的代价,我们可以选择任意的两个点开始进入,或者直接选择1号点(但是代价可能会很大),其实它的本质还是最短路,只不过这里是要选择两个点罢了,这里的代价相当于就是边权,我们假设一个不存在的点(在路径外面),我们现在要从这个点到达1号点,要求是路径最短,那么就是一个最短路,我们最开始知道这个点到每个点的路径花费,那么我们直接存入优先队列中,后面直接是dijstra了,我们每次从花费最小的入手,现在想一下我们能够到达下一个点的条件是啥?
是不是两个点的最短路径都已经更新过才行,因为我们要更新一个点,那么只有当能够到达它的两个点的最短路都已经被更新过了才能实现,那么我们花费的问题解决了,现在来想一下方案数,这里要分两种情况,1、当这个点需要更新,那么这个点的方案数是将会被覆盖的,所以直接是能够到达它的两个点的方案数的乘积,2、当前点的最短路径恰好等于两个点能够到达它的最短路,这个时候就是累加了。

#include <bits/stdc++.h>

#define int long long

using namespace std;

const int N = 1e3 + 10, M = N * N, inf = 0x3f3f3f3f;
typedef pair<int, int> PII;

int e[M], w[M], ne[M], h[M], idx;

int dis[N], cnt[N];
bool st[N];

priority_queue<PII, vector<PII>, greater<PII>> q;

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

void dijkstra()
{
    memset(st, 0, sizeof st);

    while (!q.empty())
    {
        auto u = q.top(); q.pop();
        int ver = u.second, distance = u.first;

        if (distance != dis[ver]) continue;
        if (st[ver]) continue;
        st[ver] = 1;

        for (int i = h[ver]; i != -1; i = ne[i])
        {
            int x = e[i], v = w[i];

            if (st[x])
            {
                if (dis[v] > distance + dis[x])
                {
                    dis[v] = distance + dis[x];
                    cnt[v] = cnt[ver] * cnt[x];
                    q.push({dis[v], v});
                }
                else if (dis[v] == distance + dis[x])
                    cnt[v] += cnt[ver] * cnt[x];
            }
        }
    }

}

void solve()
{
    int n; scanf("%lld", &n);
    memset(h, -1, sizeof h);
    for (int i = 1; i <= n; i ++)
    {
        cin >> dis[i];
        cnt[i] = 1;
        q.push({dis[i], i});
    }

    int x, y, z;
    while (scanf("%lld%lld%lld", &x, &y, &z) != EOF)
    {
        add(x + 1, y + 1, z + 1);
        if (x + 1 == y + 1) continue;
        add(y + 1, x + 1, z + 1);
    }

    dijkstra();

    printf("%lld %lld\n", dis[1], cnt[1]);
}

signed main()
{
    solve();

    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值