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