相信的心是你的魔法 | 信じの心は君の魔法
题意:
- 药水可以直接买,也可以由两种其他药水配置而来,问配置出 0号药水 的最小价钱 和 最小价的方案数
思路:
- 义眼拓扑,但!在题意没有明确强调 有向无环图 时,用 拓扑 写的沙口都会吃尽苦头…
- 此题的数据是有环的,有环就代表得用最短路来写;
- 但这个由两个药水推导出下一个药水就很蛋痛,正常的最短路思维都是由一个最小推导其他
- 观察数据范围,除了 Floyd 都可用,图中的边权都为正,Dijkstra可用
- 明确 算法的本质 很重要,朴素版Dijkstra算法的贪心在此题的维护上非常方便,通过一个确定的最小来更新其他点的 dist 值
代码:
#include<bits/stdc++.h>
#include<unordered_set>
#include<unordered_map>
#define mem(a,b) memset(a,b,sizeof a)
#define cinios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
#define sca scanf
#define pri printf
#define ul (u << 1)
#define ur (u << 1 | 1)
#define fx first
#define fy second
//#pragma GCC optimize(2)
//[博客地址](https://blog.csdn.net/weixin_51797626?t=1)
using namespace std;
typedef long long ll;
typedef pair<ll, int> PII;
typedef pair <ll, PII> PI;
const int N = 1010, M = 4000010, MM = N;
int INF = 0x3f3f3f3f, mod = 100003;
ll LNF = 0x3f3f3f3f3f3f3f3f;
int n, m, k, T, S, D;
int way[N][N], cost[N], cnt[N];
bool st[N];
int main() {
cinios;
cin >> n;
for (int i = 1; i <= n; i++)cin >> cost[i], cnt[i] = 1;
//初始每个药水都有花费,方案数最少为 1
int a, b, x;
while (cin >> a >> b >> x)
{
a++, b++, x++;
way[a][b] = x;//记录组合:原料两药水 + 配得的药水
way[b][a] = x;
}
for (int i = 1; i <= n; i++) { //标准Dijkstra
int t = 0;
for (int j = 1; j <= n; j++)
if (!st[j] && (!t || cost[j] < cost[t]))//贪心取出花费最小的一个药水
t = j;
st[t] = true;//图中的边权都是正,拿出来的点必定已经被确定了,花费不能更小
for (int j = 1; j <= n; j++) { //魔改部分
int c = way[t][j];//枚举此药水与其他药水的所有匹配方案
if (st[j] && c) {
//当且仅当有匹配,且两药水都是被确定过(花费已经是最小)时
if (cost[c] > cost[t] + cost[j]) {
cost[c] = cost[t] + cost[j];//更新目标药水
cnt[c] = cnt[t] * cnt[j];
}
else if (cost[c] == cost[t] + cost[j])//同时记录方案
cnt[c] += cnt[t] * cnt[j];
}
}
}
cout << cost[1] << " " << cnt[1];
return 0;
}
/*
*/