【Usaco2014Open银组】坑爹的GPS (gpsdual) 题解
1.题意简述
有一张有向图,两种
2.样例解释
5 7
3 4 7 1
1 3 2 20
1 4 17 18
4 5 25 3
1 2 10 1
3 5 4 14
2 4 6 5
1
先根据样例画出图形:
然后我们可以算出每个点到
所以不难发现,第一个导航最希望的道路是
当然,如果
回归样例,当他走的是
3.思路
这道题很明显就是一道最短路。但是我们再做题前要先明白最短路的运行性质:
当第
当
特殊的,当两点到终点距离都被更新时,如果更新后
但是,知道了这么多,题目要怎么写呢?如何判断导航是否要警告呢?我们不难发现,当导航警告时,说明
那么现在问题又来了:我应该怎么统计最小警告总次数呢?也很简单,只需再开一张图,将每条边可能鸣笛总次数存为其边权,再跑一遍最短路即可。
然后我们就可以按照以下方式做题:
-
1.跑第一遍最短路,将第一个导航中每个点到终点的距离存在数组
中; -
2.跑第二遍最短路,将第二个导航中每个点到终点的距离存在数组
中; -
3.枚举每条边,分别判断两个导航是否需要警告,将每条边警告总次数存为其边权;
-
4.跑第三遍最短路,输出最终警告次数。
4.注意事项
-
1.在跑最短路时,因为是从
跑到 ,所以存边时要反着存,在统计答案时,因为图是反着存的,所以也是从终点出发,最后输出起点的警告次数。 -
2.每次最短路时,都要先把存图用的数组清空,避免爆炸。
-
3.因为这道题要跑三遍最短路,这里建议写一个函数,用指针分别指向三个存最短路的数组,让代码看起来更清爽。。。
5.代码:
#include <bits/stdc++.h>
// #define int long long
using namespace std;
#define N 200010
#define For(i,j,k) for(int i=j;i<=k;i++)
#define IOS ios::sync_with_stdio(),cin.tie(),cout.tie()
typedef pair <int, int> PII;
struct no {
int a, b, w1, w2;
}ed[N];
int n, m;
int idx = 0, h[N], w[N], e[N], ne[N];
int dis1[N], dis2[N], dis3[N], st[N];
void add (int a, int b, int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}
void dijkstra (int ss, int *dis) { //*dis是指针,分别指向三个存最短路的数组
For (i, 1, n) dis[i] = 0x3f3f3f3f; //指针数组不能用memset
memset (st, 0, sizeof st);
dis[ss] = 0;
priority_queue <PII, vector <PII>, greater <PII> > p;
p.push ({0, ss});
while (!p.empty ()) {
auto t = p.top ();
p.pop ();
int ver = t.second, dit = t.first;
if (st[ver]) {
continue;
}
st[ver] = true;
for (int i = h[ver]; i != -1; i = ne[i]) {
int j = e[i];
if (dis[j] > dit + w[i]) {
dis[j] = dit + w[i];
p.push ({dis[j], j});
}
}
}
} //标准模板dijkstra
signed main () {
IOS;
cin >> n >> m;
For (i, 1, m) {
int x, y, w1, w2;
cin >> x >> y >> w1 >> w2;
ed[i] = {y, x, w1, w2}; //从y连到x,注意存反边
}
memset (h, -1, sizeof h);
idx = 0; //第一次清空
For (i, 1, m) add (ed[i].a, ed[i].b, ed[i].w1); //根据第一个导航建边
dijkstra (n, dis1);
memset (h, -1, sizeof h);
idx = 0; //第二次清空
For (i, 1, m) add (ed[i].a, ed[i].b, ed[i].w2); //根据第二个导航建边
dijkstra (n, dis2);
// For (i, 1, n) {cout << dis1[i] << ' ';} cout << endl;
// For (i, 1, n) {cout << dis2[i] << ' ';} cout << endl;
memset (h, -1, sizeof h);
idx = 0; //第三次清空
For (i, 1, m) {
int cnt = 0; //存储第i条边的警告次数
if (dis1[ed[i].b] != dis1[ed[i].a] + ed[i].w1) cnt ++; //第一个导航
if (dis2[ed[i].b] != dis2[ed[i].a] + ed[i].w2) cnt ++; //第二个导航
add (ed[i].a, ed[i].b, cnt); //建边,边权为其警告次数
}
dijkstra (n, dis3); //还是倒着跑
cout << dis3[1] << endl; //输出起点的警告次数
return 0;
} //完结撒花!!!!!!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现