今天来做最短路的题目
直接看题吧 毕竟洛谷可以直接复制md文件的特性比openjudge好多了
P1462 通往奥格瑞玛的道路
题目背景
在艾泽拉斯大陆上有一位名叫歪嘴哦的神奇术士,他是部落的中坚力量。
有一天他醒来后发现自己居然到了联盟的主城暴风城。
在被众多联盟的士兵攻击后,他决定逃回自己的家乡奥格瑞玛。
题目描述
在艾泽拉斯,有 \(n\) 个城市。编号为 \(1,2,3,\ldots,n\)。
城市之间有 \(m\) 条双向的公路,连接着两个城市,从某个城市到另一个城市,会遭到联盟的攻击,进而损失一定的血量。
每次经过一个城市,都会被收取一定的过路费(包括起点和终点)。路上并没有收费站。
假设 \(1\) 为暴风城,\(n\) 为奥格瑞玛,而他的血量最多为 \(b\),出发时他的血量是满的。如果他的血量降低至负数,则他就无法到达奥格瑞玛。
歪嘴哦不希望花很多钱,他想知道,在所有可以到达奥格瑞玛的道路中,对于每条道路所经过的城市收费的最大值,最小值为多少。
输入格式
第一行 \(3\) 个正整数,\(n,m,b\)。分别表示有 \(n\) 个城市,\(m\) 条公路,歪嘴哦的血量为 \(b\)。
接下来有 \(n\) 行,每行 \(1\) 个正整数,\(f_i\)。表示经过城市 \(i\),需要交费 \(f_i\) 元。
再接下来有 \(m\) 行,每行 \(3\) 个正整数,\(a_i,b_i,c_i\)(\(1\leq a_i,b_i\leq n\))。表示城市 \(a_i\) 和城市 \(b_i\) 之间有一条公路,如果从城市 \(a_i\) 到城市 \(b_i\),或者从城市 \(b_i\) 到城市 \(a_i\),会损失 \(c_i\) 的血量。
输出格式
仅一个整数,表示歪嘴哦交费最多的一次的最小值。
如果他无法到达奥格瑞玛,输出 AFK
。
输入输出样例 #1
输入 #1
4 4 8
8
5
6
10
2 1 2
2 4 1
1 3 4
3 4 3
输出 #1
10
说明/提示
对于 \(60\%\) 的数据,满足 \(n\leq 200\),\(m\leq 10^4\),\(b\leq 200\);
对于 \(100\%\) 的数据,满足 \(1\leq n\leq 10^4\),\(1\leq m\leq 5\times 10^4\),\(1\leq b\leq 10^9\);
对于 \(100\%\) 的数据,满足 \(1\leq c_i\leq 10^9\),\(0\leq f_i\leq 10^9\),可能有两条边连接着相同的城市。
Solution
其实一开始我是不大理解题目的 什么叫“对于每条道路经过的的城市收费的最大值,最小值是多少”
就算看不懂也知道这道题要二分()
好啦,其实意思就是 对于每条到终点的路径中,每条路径被收的钱的最大值,在所有路径中取最小值
那二分就是这样的 你想好check函数 然后l和r微调就能出来
主要是昨天写的时候出现一堆很唐的错误
我们对于check函数 其实就是跑一遍最短路(这里用的Dijkstra),如果碰到比需要的值价钱高的节点就不经过,然后如果扣血会死就返回false 否则返回true
为什么要这么干 因为对于每个给定的mid值 我们只需找出一条合法路径 使得该路径上每个点的价格都不高于mid,这样就可以了
不过,这道题有个特点,就是因为它的值是离散化的,所以最终答案一定在给定的点的价钱中出现,可以排序后按序号二分,时间会快很多
而且由于1和n必须经过,所以如果是用连续二分的话,l最好赋值成max(price[1],price[n])
记得开long long
下面看代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,tot,m,b;
int x,y,z;
int head[200005],nex[200005],ver[200005],edge[200005];
ll price[20005];
int vis[20005];
ll d[20005];
priority_queue<pair<ll,int>>q;
ll fprice[20005];
void add(int x,int y,int z){ver[++tot]=y;edge[tot]=z;nex[tot]=head[x];head[x]=tot;
}
bool check(ll t){memset(vis,0,sizeof(vis));for(int i=2;i<=n;i++) d[i]=1e9;d[1]=0;if(price[1]>t||price[n]>t) return false;q.push(make_pair(0,1));while(!q.empty()){int x=q.top().second;q.pop();if(vis[x]) continue;vis[x]=1;for(int i=head[x];i;i=nex[i]){int y=ver[i],z=edge[i];if(price[y]>t){continue;}if(d[y]>d[x]+z){d[y]=d[x]+z;q.push(make_pair(-d[y],y));}}}if(d[n]>b) return false;else return true;
}
void dijkstra(){memset(vis,0,sizeof(vis));for(int i=2;i<=n;i++) d[i]=1e9;d[1]=0; q.push(make_pair(0,1));while(!q.empty()){int x=q.top().second;q.pop();if(vis[x]) continue;vis[x]=1;for(int i=head[x];i;i=nex[i]){int y=ver[i],z=edge[i];if(d[y]>d[x]+z){d[y]=d[x]+z;q.push(make_pair(-d[y],y));}}}
}
int main(){scanf("%d%d%d",&n,&m,&b);for(int i=1;i<=n;i++){scanf("%lld",&price[i]);}for(int i=1;i<=n;i++) fprice[i]=price[i];sort(fprice+1,fprice+n+1);for(int i=1;i<=m;i++){scanf("%d%d%d",&x,&y,&z);add(x,y,z);add(y,x,z);}dijkstra();if(d[n]>b){printf("AFK");system("pause");return 0;}int l=1,r=n,mid;while(l<r){mid=(l+r)/2;if(check(fprice[mid])){r=mid;}else{l=mid+1;}}printf("%lld",fprice[l]);system("pause");return 0;
}