洛谷P1462spfa + 二分答案
第一次接触二分答案的题目最开始是没有思路的看了一个题解,然后强行理解之后开始自己打了一遍,然而结果是只得了30分过了3个点其他全wa,之后是漫长的debug,这里想感慨一句自己debug的速度是真慢。先把题目之间拉过来吧。
通往奥格瑞玛的道路
题目背景
在艾泽拉斯大陆上有一位名叫歪嘴哦的神奇术士,他是部落的中坚力量。
有一天他醒来后发现自己居然到了联盟的主城暴风城。
在被众多联盟的士兵攻击后,他决定逃回自己的家乡奥格瑞玛。
题目描述
在艾泽拉斯,有
城市之间有
每次经过一个城市,都会被收取一定的过路费(包括起点和终点)。路上并没有收费站。
假设
歪嘴哦不希望花很多钱,他想知道,在可以到达奥格瑞玛的情况下,他所经过的所有城市中最多的一次收取的费用的最小值是多少。
输入格式
第一行
接下来有
再接下来有
输出格式
仅一个整数,表示歪嘴哦交费最多的一次的最小值。
如果他无法到达奥格瑞玛,输出 AFK
。
样例 #1
样例输入 #1
4 4 8
8
5
6
10
2 1 2
2 4 1
1 3 4
3 4 3
样例输出 #1
10
提示
对于
对于
对于 的数据,满足 , ,可能有两条边连接着相同的城市。
思路:
几个关键点和题目的理解
1、 我们有两个关键的量,一个是血量,另一个是金钱。
2、在可以到达奥格瑞玛的情况下,他所经过的所有城市中最多的一次收取的费用的最小值是多少对于这句话的理解,就是我们先有一条能到达终点的路径,在这个路径上我们找到这条路径上收取费用最多的城市。然后能到终点的路径有多条,所以这个收取费用最多的城市也有多个,我们要找到的就是最小的那一个。
3、我们如果要枚举每一个答案的话时间复杂度就是n,而最短路算法的时间复杂度,dijistkal是nlogn,而spfa出题人不卡的话一般是km, k为常数,如果出题人卡数据的话会退化为mn,暴力枚举的话时间复杂度就是n^2logn或者nm,是过不了的,
4、题目中出现最大值最小,一般考虑二分答案,这是我们看一下答案是否是具有单调性的呢,(目前还没想清楚单调性是怎么回事)
还要说一下自己打代码过程和debug中找到自己的问题,1、数据范围边界设置的不合理因为题目中的数据大于int这时设置正无穷就应该更大.而且需要注意会爆int 2、spfa的模版不熟悉还是有不理解的地方,比如使用队列中的点去进行松弛操作,更新它能更新的所有点这一点自己写的时候却写错了,而且看了好多遍竟然都没发现,最后才找到。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
#include <queue>
using namespace std;
typedef long long ll;
const ll N = 1e5 + 10, M = 1e6 + 10, INF = 0x7fffffff;
//用来跑最短路
bool inq[N], vis[N];
ll a[N], l, r;
ll n, m, hp;
ll dist[N];
//链式前向星
ll h[N], e[N], ne[N], w[N], idx;
void add(ll a, ll b, ll c)
{
w[idx] = c; e[idx] = b; ne[idx] = h[a]; h[a] = idx ++;
}
//spfa算法
bool spfa(ll x)
{
for(ll i = 1; i <= n ; ++ i)
{
inq[i] = false;
if(a[i] <= x) vis[i] = false;
else vis[i] = true;
}
for(int i = 2; i <= n; ++ i) dist[i] = 0x7fffffff;
queue<ll> q;q.push(1);inq[1] = true;
while(q.size())
{
ll t = q.front();q.pop();
if(vis[t]) continue;
inq[t] = false;
for(ll i = h[t]; i != -1; i = ne[i])
{
ll j = e[i];
if(vis[j]) continue;
if(dist[j] > dist[t] + w[i])
{
dist[j] = dist[t] + w[i];
if(!inq[j])
{
q.push(j);
inq[j] = true;
}
}
}
}
return dist[n] <= hp;
}
int main()
{
//文件读入数据
freopen("1.in.txt","r",stdin);
//初始化表头
memset(h, -1, sizeof h);
scanf("%lld%lld%lld", &n, &m, &hp);
for(ll i = 1; i <= n; ++ i) {scanf("%lld", &a[i]); r = max(r, a[i]);}
for(ll i = 1; i <= m; ++ i)
{
ll a, b, c;scanf("%lld%lld%lld", &a, &b, & c);
add(a, b, c); add(b, a, c);
}
//当最短路不存在时我们直接输出AFK返回;
if(!spfa(1000000005))
{
cout << "AFK" << endl;
return 0;
}
//二分答案,找到能跑最短路的最小的答案,大于答案的点是不能走到的点在跑最短路时这些点不能经过
while(l < r)
{
ll mid = (l + r ) >> 1;
if(spfa(mid)) r = mid;
else l = mid + 1;
}
cout << l;
return 0;
}
关于spfa算法
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N = 1e5 + 10, M = 2 * N;
int n, m;
int h[N], e[M], w[M], ne[M], idx;
void add(int a, int b, int c)
{
w[idx] = c; e[idx] = b; ne[idx] = h[a]; h[a] = idx ++;
}
int d[N];
bool inq[N];
void spfa()
{
queue<int> q;
memset(d, 0x3f, sizeof d);
memset(inq, 0, sizeof inq);
d[1] = 0; q.push(1); inq[1] = 1;
while(q.size())
{
auto t = q.front(); q.pop(); inq[t] = 0;
for(int i = h[t]; i != -1; i = ne[i])
{
int j = e[i];
if(d[j] > d[t] + w[j])
{
d[j] = d[t] + w[j];
if(!inq[j])
{
inq[j] = 1;
q.push(j);
}
}
}
}
}
int main()
{
memset(h, -1, sizeof h);
cin >> n >> m;
for(int i = 1; i <= m; ++ i)
{
int a, b, c; cin >> a >> b >> c;
add(a, b, c);
}
spfa();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署