浅谈SPFA——洛谷P1576 最小花费 题解
想找原题请点击这里:传送门
原题:
题目描述
在n个人中,某些人的银行账号之间可以互相转账。这些人之间转账的手续费各不相同。给定这些人之间转账时需要从转账金额里扣除百分之几的手续费,请问A最少需要多少钱使得转账后B收到100元。 输入格式
第一行输入两个正整数n,m,分别表示总人数和可以互相转账的人的对数。 以下m行每行输入三个正整数x,y,z,表示标号为x的人和标号为y的人之间互相转账需要扣除z%的手续费 (z<)。 最后一行输入两个正整数A,B。数据保证A与B之间可以直接或间接地转账。 输出格式
输出A使得B到账100元最少需要的总费用。精确到小数点后8位。 输入输出样例
输入 复制 输出 复制
103.07153164
说明/提示
<=n<=,m<=
首先看到这道题就想到是一道图论问题,并且类似最短路问题。但是不同的是我们这里求的不是最短路,而是最长路。
那么最短路径问题的话ROS首先想到dijkstra,所以在一开始便使用了dijkstra算法(思路一会儿再讲),然而写的dijkstra算法却只能得20分,剩下的点全部TLE。ROS一会儿在分析时会讲为何这道题如同ROS所写的dijkstra会TLE掉。
那么首先先分析一下这道题吧:
这道题我们看题目便发现是一道类单元最短路径问题。我们知道要从A向B转账100元,如我们需要计算从A向B转账X元实际到账多少则是需要计算从A到Bの”实际到账率“(即 ∏(1-手续率))。然后我们再用100元除以从A到B的最大的实际到账率,则可以算出来我们需要的最少的钱数了。
如果说我们使用dijkstra算法的话,开始则需要使一开始所有点的dis均为-1(此处的dis要为double数据类型,用来储存”实际到账率)。然后令dis[a]=1(即从A向A汇款实际到账率为100%,便于后面的计算)。然后用链式前向星一条条枚举以A为起点的边。并且当dis[tmp.to]<dis[nowid]*e[i].w时,令dis[tmp.to]=dis[nowid]*e[i].w。并且这一过程需要使用优先队列堆优化,但是我们很快就会发现这一过程的问题:Dijkstra算法在此处的体现即为我们从堆中不断取出两点间距离最大的线路并试图更新。但由于我们的dis是由路径上的几个最大的实际到账率相乘得到的的,故我们并不能保证几个最大的实际到账率相乘之后就一定大于几个较小的实际到账率相乘的值。比如说:0.9*0.85*0.8*0.75*0.7=0.3213,而0.65*0.6=0.39。所以说此处的dijkstra的堆优化毫无作用,使得Dijkstra退化成了O(n²)的最朴素的Dijkstra,所以计算时间便会长很多。
而由于“SPFA它死了”(SPFA算法在2018NOI D1T1中被数据卡死只能得20分。然后讲题人在讲题的时候屏幕上放着:“关于SPFA:它死了”)ROS以前在学习的时候认为Dijkstra是万能的所以ROS便没有深入学习SPFA算法。后来得知SPFA能够处理负边权问题并且在一些情况下SPFA能够处理Dijkstra不能够处理的问题(比如这道),所以ROS便考虑学习一下SPFA算法。
SPFA是什么?
SPFA是中国人自己发明的算法!
SPFA算法求最短路径的流程如下:
对于一个图来说,朴素的SPFA算法中创造一个队列。将每个点的dis值全部设置为0x3f(很大的数,可理解为无限大)然后用类似BFS的原理,将已经入队的此元素找到相应的后驱边。然后比较后驱点点dis值与上一个点的dis值+这条边的边权;如果此后驱点的dis值大于上一个点的dis值+这条边的边权,则更新dis值并且如果该顶点此时未入队,将其入队。所以这一过程的终点就是当队列为空时,操作结束。
这种做法显然可以处理负边权的情况,由于即使某一个点在之前被更新过,但由于SPFA算法的原理所以依然会进行比较从而试图更新,故可处理负边权。
代码实现:
#include<bits/stdc++.h>
#define N 2005
#define M 100005
using namespace std;
double dis[N];
bool vis[N];
int n,m;
int x,y;
double z;
int a,b;
int tot;
int head[N];
struct tree{
int to;
int nxt;
double w;
}e[*M];
struct node{
int f; //现在在几号点
double g; //现在的距离
};
queue <node> q;
void add(int u,int v,double r){
e[++tot].to=v;
e[tot].nxt=head[u];
head[u]=tot;
e[tot].w=(-r)/;
}
void SPFA(){
node tmp=(node){a,};
dis[a]=;
q.push(tmp);
while(!q.empty()){
tmp=q.front();
q.pop();
vis[tmp.f]=false;
for(int i=head[tmp.f];i;i=e[i].nxt){
int vv=e[i].to;
if(dis[vv]<dis[tmp.f]*e[i].w){
dis[vv]=dis[tmp.f]*e[i].w;
if(!vis[vv]){
vis[vv]=true;
q.push((node){vv,dis[vv]});
}
}
}
}
return ;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++) dis[i]=-;
for(int i=;i<=m;i++){
scanf("%d%d%lf",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
scanf("%d%d",&a,&b);
SPFA();
printf("%.8lf",/dis[b]);
return ;
}
浅谈SPFA——洛谷P1576 最小花费 题解的更多相关文章
- 洛谷—— P1576 最小花费
P1576 最小花费 题目背景 题目描述 在n个人中,某些人的银行账号之间可以互相转账.这些人之间转账的手续费各不相同.给定这些人之间转账时需要从转账金额里扣除百分之几的手续费,请问A最少需要多少钱使 ...
- 洛谷 P1576 最小花费
题目戳 题目描述 在n个人中,某些人的银行账号之间可以互相转账.这些人之间转账的手续费各不相同.给定这些人之间转账时需要从转账金额里扣除百分之几的手续费,请问A最少需要多少钱使得转账后B收到100元. ...
- 洛谷P1576||最小花费||dijkstra||双向建边!!
题目描述 在n个人中,某些人的银行账号之间可以互相转账.这些人之间转账的手续费各不相同.给定这些人之间转账时需要从转账金额里扣除百分之几的手续费,请问A最少需要多少钱使得转账后B收到100元. 数据范 ...
- 洛谷P1576 最小花费x
题目背景 题目描述 在n个人中,某些人的银行账号之间可以互相转账.这些人之间转账的手续费各不相同.给定这些人之间转账时需要从转账金额里扣除百分之几的手续费,请问A最少需要多少钱使得转账后B收到100元 ...
- 浅谈分治 —— 洛谷P1228 地毯填补问题 题解
如果想看原题网址的话请点击这里:地毯填补问题 原题: 题目描述 相传在一个古老的阿拉伯国家里,有一座宫殿.宫殿里有个四四方方的格子迷宫,国王选择驸马的方法非常特殊,也非常简单:公主就站在其中一个方格子 ...
- 洛谷 P1756 最小花费
题目背景 题目描述 在n个人中,某些人的银行账号之间可以互相转账.这些人之间转账的手续费各不相同.给定这些人之间转账时需要从转账金额里扣除百分之几的手续费,请问A最少需要多少钱使得转账后B收到100元 ...
- 洛谷P2085最小函数值题解
题目 首先我们先分析一下题目范围,\(a,b,c\) 都是整数,因此我们可以得出它的函数值在\((0,+\infty )\)上是单调递增的,,然后我们可以根据函数的性质,将每个函数设置一个当前指向位置 ...
- 洛谷4951 地震 bzoj1816扑克牌 洛谷3199最小圈 / 01分数规划
洛谷4951 地震 #include<iostream> #include<cstdio> #include<algorithm> #define go(i,a,b ...
- 洛谷P2832 行路难 分析+题解代码【玄学最短路】
洛谷P2832 行路难 分析+题解代码[玄学最短路] 题目背景: 小X来到了山区,领略山林之乐.在他乐以忘忧之时,他突然发现,开学迫在眉睫 题目描述: 山区有n座山.山之间有m条羊肠小道,每条连接两座 ...
随机推荐
- layer.open 回调函数
官方资料:http://www.layui.com/doc/modules/layer.html 在一个弹出框中新增个按钮,点击按钮后执行自己的语句(返回上一页并刷新). layer.open({ti ...
- Unity相机鼠标基本控制
一.滚轮控制视角缩放 /// <summary> /// 滚轮控制相机视角缩放 /// </summary> public void CameraFOV() { //获取鼠标滚 ...
- 如何在linux安装ruby2.2.2+
背景: 想搭建redis集群但是提示需要ruby2.2.2+ 直接使用yum安装 yum -y install ruby ruby-devel rubygems rpm-build 使用ruby -v ...
- 「题解」「CF468D」树中的配对
目录 题目大意 思路 源代码 本博客除代码之外,来自 skylee 大佬. 题目大意 一棵\(n(n\le10^5)\)个编号为\(1\sim n\)的点的带边权的树,求一个排列\(p_{1\sim ...
- 1.java-谈谈接口
1.面向接口给程序带来的便利和灵活性 List li = new ArrayLIst(); 为什么不写成 ArrayList li = new ArrayLIst(); 2.接口就相当于一些类的规范, ...
- linux 复制系统盘 dd
lsblk sudo dd if=/dev/mmcblk0 of=/dev/sda bs=8M sudo watch -n 5 pkill -USR1 ^dd$
- P1558 色板游戏 线段树(区间修改,区间查询)
题意: 给n,m,k,n长度,k个操作,m种颜色 操作C:输入A,B,C,区间[A,B]变成C颜色,可能A>B,所以要确保A<B 操作P:输入A,B,区间[A,B]的颜色种类 思路: 因为 ...
- 前端——语言——Core JS——《The good part》读书笔记——第九,十章节(Style,Good Features)
第九章节 本章节不再介绍知识点,而是作者在提倡大家培养良好的编码习惯,使用Good parts of JS,避免Bad parts of JS.它是一篇文章. 本文的1-3段阐述应用在开发过程中总会遇 ...
- Flutter 开发入门实践
前言: Flutter 是 Google 推出的跨平台解决方案, 开发语言:Dart 优势: 劣势: 学习推荐: 官方网站:https://flutter.io/ 书籍:<Flutter技术入门 ...
- token是个什么东西?怎样生成并携带token
什么是token及怎样生成token 转载自:https://www.cnblogs.com/lufeiludaima/p/pz20190203.html 什么是token Token是服务端生成的 ...