洛谷:P1875 佳佳的魔法药水(有向有环图 + Dijkstra变式)

相信的心是你的魔法 | 信じの心は君の魔法

在这里插入图片描述

题意:

  • 药水可以直接买,也可以由两种其他药水配置而来,问配置出 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;
}
/*
*/
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值