浅谈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条羊肠小道,每条连接两座 ...
随机推荐
- opencv:霍夫圆检测
#include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace st ...
- 快捷键(二):VSCode
1,打开命令板:F1或Ctrl+Chift+P 2,新建文件:Ctrl+N 3,文件间切换:Ctrl+Tab 4,新开界面:Ctrl+Shift+N 5,关闭当前窗口:Ctrl+W 6,关闭界面:Ct ...
- angular 输入框自动绑定值最长为16位,超过16位则会报错
最近发现angular在使用input输入框的ng-model绑定scope变量的时候,发现,输入长串的数字将会出错.代码如下: <html> <head> <meta ...
- Spring AOP编程(二)-AOP实现的三种方式
AOP的实现有三种方式: l aop底层将采用代理机制进行实现. l 接口 + 实现类 :spring采用 jdk 的动态代理Proxy. l 实现类: ...
- 寒假安卓app开发学习记录(2)
今天属实是头疼的一天.开始的时候是简单了解了一下安卓的系统架构,了解到大概分为四个部分. 然后看了两节创建安卓项目的课程,准备去实践一下的时候突然发现我的eclipse里竟然没有Android选项.查 ...
- Go_bufio包
bufio 是通过缓冲来提高效率. io操作本身的效率并不低,低的是频繁的访问本地磁盘的文件.所以bufio就提供了缓冲区(分配一块内存),读和写都先在缓冲区中,最后再读写文件,来降低访问本地磁盘的次 ...
- 学会C#可以做什么
C#基于.NET Framework 和 .NET CORE平台 Client/Server 客户端/服务端 windows桌面应用程序 winform 2D WPF 3D Browser/Se ...
- Python环境搭建后,多种方式的使用进行程序的执行。
Python环境搭建后,可以使用多种方式进行程序的执行. 第一种: 进入CMD命令提示符 输入python 进入python环境(可以使用Ctrl+C退出) 输入print("hello&q ...
- Dart语言学习(三)Dart数值型
一.类型 数值型有 num,int, double num a = ; a = 12.5; print(a); print(a.runtimeType); ; // b = 20.5; print( ...
- Mysql与PostgreSql数据库学习笔记
mysql 从最基础的数据引擎,到进程结构,都不能支持数据版本.导致其职能阻塞“并发”,不支持最基本的事务,innodb达不到基本事务要求,任何写数据,都导致整个表锁住.充其量只能算是一个玩具,或者说 ...