[无聊测试赛] T8 佳佳的魔法药水

思路比较新奇,但是想到了题目还是挺好做的

看到最小和次数可以想到最短路.看到数据范围可以想到必须在跑dij的时候记录次数.由于要记录的是最短路的方案,易证如果一种药有一种更便宜的配置方法,我们不会记录贵的那种.

于是,我们可以将这道题转化为一个用堆优化的dij.如果一种药水在堆顶,那么这种药水不可能有更便宜的配置方法

如果由A药水和B药水合成C药水比之前找到的配置方法便宜,那么C药水的配置方法数量为 A药水的配置方法 \(*\) B药水的配置方法.

如果由A药水和B药水合成C药水等于之前找到的配置方法,那么C药水的配置方法数量为 C药水的配置数量 \(+\) A药水的配置方法 \(*\) B药水的配置方法.

在遍历图的时候,只有两种药水都被证实为最小花费,他们合成的药水才会是他们能得到的最小值.要证明这两种的最小花费的方法是:如果他俩在堆里出现过,那么他们一定是最小花费.这个操作可以用vis数组实现

遍历整张图,最后到0的距离+配置0的方案数就是答案

#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
#define pp pair<int,int>
#define f first
#define s second
const int MAXN = 1e3+5;
vector<pp> adj[MAXN];
priority_queue<pp> q;
int n;
int dist[MAXN], ways[MAXN],ans1,ans2,in[MAXN];
bool vis[MAXN];
int main(){
  cin >> n;
  for (int i=0;i<n;i++){
    cin >> dist[i];
    ways[i] = 1;//一开始都只有一种
    q.push(make_pair(-dist[i],i));//扔进堆
  }
  ans1 = dist[0]; ans2 = 1;
  int a,b,c;
  while(cin >> a >> b >> c){
    adj[a].push_back(make_pair(b,c));
    if(a!=b)adj[b].push_back(make_pair(a,c));//注意,他可能出现A+A=C.这种情况不需要连两次边
  }
  while(!q.empty()){
    int qf = -q.top().f, qs = q.top().s; q.pop();
    if (dist[qs]!=qf) continue;//如果一个点的最小距离不等于在堆顶的距离,说明这个点已经更新过了,不需要再更新
    vis[qs] = true;//这个点被拿过了
    for (pp v : adj[qs]){
      if (!vis[v.f]) continue;//如果另一种药水没被拿过,我们不能确定现在他的距离是最小的
      if (dist[v.s]>qf+dist[v.f]){
        dist[v.s] = qf+dist[v.f];
        ways[v.s] = ways[v.f]*ways[qs];
        q.push(make_pair(-dist[v.s],v.s));//刚刚讲过的转移
      }else if (dist[v.s]==qf+dist[v.f]){
        ways[v.s] += ways[v.f]*ways[qs];
      }
    }
  }
  cout << dist[0] << " " <<ways[0];//答案
}

原文地址:https://www.cnblogs.com/DannyXu/p/12536356.html

时间: 2024-11-09 16:24:46

[无聊测试赛] T8 佳佳的魔法药水的相关文章

无聊测试赛题解

无聊测试赛题解 首先膜拜_Zhumingrui大佬获得1000分的好成绩 T1 凯撒密码 T2 积木城堡 T3 出租车拼车 T4 汽车拉力比赛 T5 最佳课题选择 T6 排行榜 T7 豪华游轮 T8 佳佳的魔法药水 T9 矩阵覆盖 待填充 T10 所陀门王的宝藏 T11 老C的键盘 待填充 T12 道路 待填充 原文地址:https://www.cnblogs.com/DannyXu/p/12536283.html

[无聊测试赛] T1 凯撒密码

本题的思路大致为首先解出每个字母所移动的格子数n,然后将所有的字母移动n个格子. n的解法: 第一行出现最多的字母和E的差就是n(题目后面的提示跳舞小人bfs证明可以理解为出现的频率) 具体实现为利用map存第一行每个字符出现的次数,然后移动全盘就行了 c++的好处在于他有内在的toupper(),tolower()和isalpha().在这题要注意答案全部需要转化为大写.我的解法是直接将输入转化为大写再统计 #include <cctype> #include <iostream>

[无聊测试赛] T4 汽车拉力比赛

这题有两个思路,第一个是并查集,第二是二分.我写的二分,复杂度 \(O(n^2logn)\) 可以过 二分的方法:二分高度差别.然后从某个点起点开始整个图.如果能够将所有起点覆盖,那么证明这个高度可以实现 遍历图可以用bfs实现.这题码量稍大,但思路清晰 #include <iostream> #include <algorithm> #include <queue> using namespace std; #define pp pair<int,int>

[无聊测试赛] T3 出租车拼车

又是一个典型的背包.注意贪心思路:如果你要做这辆车,你就坐满他. \(i,j\)分别存坐上去的人总人数和现在的时间\(,dp[i][j]\) 统计来到这个状态的最小钱数 #include <iostream> #include <algorithm> #include <cstring> using namespace std; const int MAXN = 1e2+5; int n,k,d,s; int dp[MAXN<<1][MAXN],ans =

[无聊测试赛] T7 豪华游轮

观察题目,发现可以贪心能往前走多少往前走多少,然后尽量扭180度后退.剩下角度可以通过原地扭屁股实现,并不会影响距离 开两个变量分别记录往前走和往后走的距离.角度只需一个数组,往左记为 \(\theta\) ,往右记为 \(360-\theta\) 度 怎么找最接近 \(180\) ?背包... 答案怎么找?假设我们从 \((0,0)\) 出发,如果找到终点的 \(x\) 和 \(y\) 就能用勾股定理求出距离.x的值为往前走的值 * 往后走的值 \(cos(\theta)\),y的值为往后走的

[无聊测试赛] T5 最佳课题选择

又是背包...(这题比黄还简单) 一维dp. \(i\) 表示现在拿了课题数, \(dp[i]\) 表示拿到n个课题的最小花费.将dp初始化大一点就行,记得开longlong #include <iostream> #include <algorithm> #include <math.h> #include <cstring> using namespace std; const long long MAXN = 2e2+5; long long n,m;

[无聊测试赛] T10 所驼门王的宝藏

码农题.思维难度中等,难在调试 建边方法:分别将x和y排序,对每个点,对它旁边所有的x/y连边(如果他需要). 开map记录点的位置,对于每个点,如果他的九宫格之内有其他点,就对他连边 注意排序会使点的顺序混乱,故需要在状态里将点的id保存 连完边跑tarjan缩点.易证如果你能到一个环内的某个点,你就可以到达这个环内的每个点.每个环的权值为他里面的点数量.将各个环转化为一个新图 可以发现新图已经转化为一个DAG.可以选择用dfs/topo来记录答案.这里我选择用dfs. dfs每个未经过的点并

洛谷—— P1875 佳佳的魔法药水

P1875 佳佳的魔法药水 题目描述 发完了 k 张照片,佳佳却得到了一个坏消息:他的 MM 得病了!佳佳和大家一样焦急 万分!治好 MM 的病只有一种办法,那就是传说中的 0 号药水 ……怎么样才能得到 0 号药 水呢?你要知道佳佳的家境也不是很好,成本得足够低才行…… 题目描述: 得到一种药水有两种方法:可以按照魔法书上的指导自己配置,也可以到魔法商店里去 买——那里对于每种药水都有供应,虽然有可能价格很贵.在魔法书上有很多这样的记载: 1 份 A 药水混合 1 份 B 药水就可以得到 1

P1875 佳佳的魔法药水

P1875 佳佳的魔法药水 题目描述 发完了 k 张照片,佳佳却得到了一个坏消息:他的 MM 得病了!佳佳和大家一样焦急 万分!治好 MM 的病只有一种办法,那就是传说中的 0 号药水 ……怎么样才能得到 0 号药 水呢?你要知道佳佳的家境也不是很好,成本得足够低才行…… 题目描述: 得到一种药水有两种方法:可以按照魔法书上的指导自己配置,也可以到魔法商店里去 买——那里对于每种药水都有供应,虽然有可能价格很贵.在魔法书上有很多这样的记载: 1 份 A 药水混合 1 份 B 药水就可以得到 1