想找原题请点击这里:传送门

原题:

题目描述
在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 最小花费 题解的更多相关文章

  1. 洛谷—— P1576 最小花费

    P1576 最小花费 题目背景 题目描述 在n个人中,某些人的银行账号之间可以互相转账.这些人之间转账的手续费各不相同.给定这些人之间转账时需要从转账金额里扣除百分之几的手续费,请问A最少需要多少钱使 ...

  2. 洛谷 P1576 最小花费

    题目戳 题目描述 在n个人中,某些人的银行账号之间可以互相转账.这些人之间转账的手续费各不相同.给定这些人之间转账时需要从转账金额里扣除百分之几的手续费,请问A最少需要多少钱使得转账后B收到100元. ...

  3. 洛谷P1576||最小花费||dijkstra||双向建边!!

    题目描述 在n个人中,某些人的银行账号之间可以互相转账.这些人之间转账的手续费各不相同.给定这些人之间转账时需要从转账金额里扣除百分之几的手续费,请问A最少需要多少钱使得转账后B收到100元. 数据范 ...

  4. 洛谷P1576 最小花费x

    题目背景 题目描述 在n个人中,某些人的银行账号之间可以互相转账.这些人之间转账的手续费各不相同.给定这些人之间转账时需要从转账金额里扣除百分之几的手续费,请问A最少需要多少钱使得转账后B收到100元 ...

  5. 浅谈分治 —— 洛谷P1228 地毯填补问题 题解

    如果想看原题网址的话请点击这里:地毯填补问题 原题: 题目描述 相传在一个古老的阿拉伯国家里,有一座宫殿.宫殿里有个四四方方的格子迷宫,国王选择驸马的方法非常特殊,也非常简单:公主就站在其中一个方格子 ...

  6. 洛谷 P1756 最小花费

    题目背景 题目描述 在n个人中,某些人的银行账号之间可以互相转账.这些人之间转账的手续费各不相同.给定这些人之间转账时需要从转账金额里扣除百分之几的手续费,请问A最少需要多少钱使得转账后B收到100元 ...

  7. 洛谷P2085最小函数值题解

    题目 首先我们先分析一下题目范围,\(a,b,c\) 都是整数,因此我们可以得出它的函数值在\((0,+\infty )\)上是单调递增的,,然后我们可以根据函数的性质,将每个函数设置一个当前指向位置 ...

  8. 洛谷4951 地震 bzoj1816扑克牌 洛谷3199最小圈 / 01分数规划

    洛谷4951 地震 #include<iostream> #include<cstdio> #include<algorithm> #define go(i,a,b ...

  9. 洛谷P2832 行路难 分析+题解代码【玄学最短路】

    洛谷P2832 行路难 分析+题解代码[玄学最短路] 题目背景: 小X来到了山区,领略山林之乐.在他乐以忘忧之时,他突然发现,开学迫在眉睫 题目描述: 山区有n座山.山之间有m条羊肠小道,每条连接两座 ...

随机推荐

  1. [Python]python中assert和isinstance的用法

    assert语句是一种插入调试断点到程序的一种便捷的方式. assert == assert == True assert ( == ) print('-----------') assert ( = ...

  2. static的使用总结

    全局静态变量 全局变量前加上关键字static,全局变量就定义成一个全局静态变量.,全局静态变量存储在静态存储区,在整个程序运行期间一直存在.全局静态变量在程序运行之前就存在. 初始化:未经初始化的全 ...

  3. c++中const变量定义与头文件包含的有关问题

    在使用C++进行程序开发的时候,有个常识我们很熟悉,就是把类的定义写在.h文件中,把类的具体实现写在.cpp文件中.这毫无疑问是对的.但我们很少去思考为什么要这样做,本文结合自己的学习体会,对头文件及 ...

  4. 把jar包部署为linux服务

    一直未配置成功,直到放弃后reboot了下,才直到错的不是自己的配置,而是自己不懂 1.在touch /etc/rc.d/init.d/tl_c_cons_service(创建新文件) 2.vi /e ...

  5. vue axios使用

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. Ansible - 配置文件

    概述 再水一发 ref Ansible Configuration Settings 1. 查看 概述 查看 配置文件 的默认位置 命令 > ansible --version 结果 confi ...

  7. Docker配置阿里云镜像源

    Docker默认拉取镜像是从这里拉取(https://hub.docker.com/),拉取的速度让人...,所以是配置国内镜像源,拉取速度十分惊人 一.版本要求 Docker版本:1.10以上 二. ...

  8. 搭建 VUE + NODE.JS + ElementUI 学习过程中问题总结

    1.exports 和 module.exports require 用来加载代码,而 exports 和 module.exports 则用来导出代码. module.exports 初始值为一个空 ...

  9. 线上BUG定位神器(阿尔萨斯)-Arthas2019-0801

    1.下载这个jar 2.运行这个jar 3.选取你需要定位的问题应用进程 然后各种trace -j xx.xxx.xx.className methodName top -n 3 这个后面要补充去看, ...

  10. JAVA基础学习(1)之计算

    1.0计算机与编程语言 1.0.1计算机与编程语言 计算机解决问题 人:What to do 计算机:How to do 编程语言 描述要求计算机如何做事情的过程或方法 算法 计算=算法—>编程 ...