浅谈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条羊肠小道,每条连接两座 ...
随机推荐
- docker容器 - 导入容器、导出容器、查看容器
实验环境 CentOS 7.5 容器 容器是镜像的运行实例.不同的是,镜像是静态的只读文件,而容器带有运行时需要的可写文件层:同时,容器中的应用进程处于运行状态. 导入和导出容器 实现容器的迁移. 导 ...
- Java+Selenium+Testng自动化测试学习(二)
Java+Selenium+TestNG自动化测试框架整合 1.简化代码 封装一个定位元素的类,类型为ElementLocation package com.test; import org.open ...
- 高斯消元-bzoj1013-球形空间产生器
This article is made by Jason-Cow.Welcome to reprint.But please post the writer's address. http://ww ...
- 数据库程序接口——JDBC——功能第二篇——数据源之C3P0数据源
综述 C3P0由三部分内容组成.实例化对象,各配置项的含义,以及加载配置项的方式. 实例化对象的方式有三种,第一种方式直接new ComboPooledDataSource,第二种方式使用工厂类Dat ...
- Dart语言学习(三)Dart数值型
一.类型 数值型有 num,int, double num a = ; a = 12.5; print(a); print(a.runtimeType); ; // b = 20.5; print( ...
- Django 生成数据库表时的报错TypeError: __init__() missing 1 required positional argument: 'on_delete'
原因及解决办法: https://www.cnblogs.com/phyger/p/8035253.html
- 各种颜色空间之间的转换算法(XYZ → Standard-RGB ,Standard-RGB → XYZ)
http://www.easyrgb.com/en/convert.php#Result http://www.easyrgb.com/en/math.php
- 松软科技web教程:JavaScript HTML DOM 元素
查找 HTML 元素 通常,通过 JavaScript,您需要操作 HTML 元素. 为了达成此目的,您需要首先找到这些元素.有好几种完成此任务的方法: 通过 id 查找 HTML 元素 通过标签名查 ...
- __dirname和__filename和process.cwd()三者的区别
1.process cwd() 方法返回 Node.js 进程当前工作的目录 例:我在F:\自己的文件\自己在网上学习的知识点\node学习\node-API\process 这个文件加下面创建了一个 ...
- 关于php/js抓取/采集
前段时间用php的一个插件(phpQuery+queryList)写了采集某个博客的一些博文,然后用linux的自动运行跑,感觉还不错. 但在很久之前就已经听说了另外一个插件,可以很好的进行采集,叫做 ...