diff --git a/TangDou/AcWing/MinimalPath/1126.cpp b/TangDou/AcWing/MinimalPath/1126.cpp index bf8886f..42bacbb 100644 --- a/TangDou/AcWing/MinimalPath/1126.cpp +++ b/TangDou/AcWing/MinimalPath/1126.cpp @@ -2,25 +2,24 @@ using namespace std; const int N = 2010; -const int M = 2e5 + 10; // 边数 +const int M = 2e5 + 10; -typedef pair PDI; +typedef pair PDI; // 堆中数值是浮点数,注意区别 -int n; // n个节点 -int m; // m条边 -double d[N]; // 从A点出发,到达每个点的最大距离 -bool st[N]; // 点i是不是已经出队列 +int n, m; +double dis[N]; +bool st[N]; int h[N], e[M], ne[M], idx; -double w[M]; +double w[M]; // 边权为浮点数,与一般的题目有区别 void add(int a, int b, double c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++; } -int s, t; +int S, T; void dijkstra() { - priority_queue q; // 大根堆 - d[s] = 1; // 剩余的百分比(想像一下手机电池,目前是100%状态出发) - q.push({1, s}); // 大根堆,按距离最大到小排序 + priority_queue q; // 大顶堆 + dis[S] = 1; + q.push({1, S}); while (q.size()) { auto t = q.top(); @@ -31,10 +30,10 @@ void dijkstra() { for (int i = h[u]; ~i; i = ne[i]) { int v = e[i]; - double a = 1 - w[i]; // 100%减去消耗率,得到本路径的剩余率,需要与带过的数据连乘 - if (d[v] < d[u] * a) { // 利用u更新j的路径最大值 - d[v] = d[u] * a; - q.push({d[v], v}); + double a = 1 - w[i]; + if (dis[v] < dis[u] * a) { + dis[v] = dis[u] * a; + q.push({dis[v], v}); } } } @@ -47,13 +46,13 @@ int main() { while (m--) { int a, b, c; cin >> a >> b >> c; - double w = c * 0.01; // 消耗的百分比,举例:从A->B的消耗百分比为2% + double w = c * 0.01; add(a, b, w), add(b, a, w); } - cin >> s >> t; + cin >> S >> T; dijkstra(); - printf("%.8lf\n", 100 / d[t]); + printf("%.8lf\n", 100 / dis[T]); return 0; } \ No newline at end of file diff --git a/TangDou/AcWing/MinimalPath/1126.md b/TangDou/AcWing/MinimalPath/1126.md index e0b2a40..eca2a8e 100644 --- a/TangDou/AcWing/MinimalPath/1126.md +++ b/TangDou/AcWing/MinimalPath/1126.md @@ -60,25 +60,24 @@ $$\large N=\frac{100}{(1−z_1\%)∗(1−z_2\%)∗…∗(1−z_n\%)}$$ using namespace std; const int N = 2010; -const int M = 2e5 + 10; // 边数 +const int M = 2e5 + 10; -typedef pair PDI; +typedef pair PDI; // 堆中数值是浮点数,注意区别 -int n; // n个节点 -int m; // m条边 -double d[N]; // 从A点出发,到达每个点的最大距离 -bool st[N]; // 点i是不是已经出队列 +int n, m; +double dis[N]; +bool st[N]; int h[N], e[M], ne[M], idx; -double w[M]; +double w[M]; // 边权为浮点数,与一般的题目有区别 void add(int a, int b, double c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++; } -int s, t; +int S, T; void dijkstra() { - priority_queue q; // 大根堆 - d[s] = 1; // 剩余的百分比(想像一下手机电池,目前是100%状态出发) - q.push({1, s}); // 大根堆,按距离最大到小排序 + priority_queue q; // 大顶堆 + dis[S] = 1; + q.push({1, S}); while (q.size()) { auto t = q.top(); @@ -89,10 +88,10 @@ void dijkstra() { for (int i = h[u]; ~i; i = ne[i]) { int v = e[i]; - double a = 1 - w[i]; // 100%减去消耗率,得到本路径的剩余率,需要与带过的数据连乘 - if (d[v] < d[u] * a) { // 利用u更新j的路径最大值 - d[v] = d[u] * a; - q.push({d[v], v}); + double a = 1 - w[i]; + if (dis[v] < dis[u] * a) { + dis[v] = dis[u] * a; + q.push({dis[v], v}); } } } @@ -105,14 +104,14 @@ int main() { while (m--) { int a, b, c; cin >> a >> b >> c; - double w = c * 0.01; // 消耗的百分比,举例:从A->B的消耗百分比为2% + double w = c * 0.01; add(a, b, w), add(b, a, w); } - cin >> s >> t; + cin >> S >> T; dijkstra(); - printf("%.8lf\n", 100 / d[t]); + printf("%.8lf\n", 100 / dis[T]); return 0; } ``` diff --git a/TangDou/AcWing/MinimalPath/1127.cpp b/TangDou/AcWing/MinimalPath/1127.cpp index 08845de..35a12eb 100644 --- a/TangDou/AcWing/MinimalPath/1127.cpp +++ b/TangDou/AcWing/MinimalPath/1127.cpp @@ -27,14 +27,14 @@ int dijkstra(int S) { PII t = q.top(); q.pop(); - int u = t.second, d = t.first; + int u = t.second; if (st[u]) continue; st[u] = true; for (int i = h[u]; ~i; i = ne[i]) { int v = e[i]; - if (dis[v] > d + w[i]) { - dis[v] = d + w[i]; + if (dis[v] > dis[u] + w[i]) { + dis[v] = dis[u] + w[i]; q.push({dis[v], v}); } } diff --git a/TangDou/AcWing/MinimalPath/1127.md b/TangDou/AcWing/MinimalPath/1127.md index 95cb525..a5a1a72 100644 --- a/TangDou/AcWing/MinimalPath/1127.md +++ b/TangDou/AcWing/MinimalPath/1127.md @@ -54,13 +54,13 @@ void add(int a, int b, int c) { int n, p, m; // 三个数:奶牛数 ,牧场数 ,牧场间道路数 int id[N]; // 每只奶牛在哪个牧场 -int d[N]; // 记录起点到任意点的最短路径 +int dis[N]; // 记录起点到任意点的最短路径 bool st[N]; // 标识每个牧场是否入过队列 int dijkstra(int S) { memset(st, 0, sizeof st); - memset(d, 0x3f, sizeof d); - d[S] = 0; + memset(dis, 0x3f, sizeof dis); + dis[S] = 0; priority_queue, greater> q; q.push({0, S}); @@ -68,23 +68,23 @@ int dijkstra(int S) { PII t = q.top(); q.pop(); - int u = t.second, dist = t.first; + int u = t.second; if (st[u]) continue; st[u] = true; for (int i = h[u]; ~i; i = ne[i]) { int v = e[i]; - if (d[v] > dist + w[i]) { - d[v] = dist + w[i]; - q.push({d[v], v}); + if (dis[v] > dis[u] + w[i]) { + dis[v] = dis[u] + w[i]; + q.push({dis[v], v}); } } } int res = 0; - for (int i = 1; i <= n; i++) { // 遍历每只奶牛 - int j = id[i]; // j号牧场 - if (d[j] == INF) return INF; // 如果j号牧场失联了,则无法获得结果 - res += d[j]; // 累加一个最小距离 + for (int i = 1; i <= n; i++) { // 遍历每只奶牛 + int j = id[i]; // j号牧场 + if (dis[j] == INF) return INF; // 如果j号牧场失联了,则无法获得结果 + res += dis[j]; // 累加一个最小距离 } return res; // 整体的最小距离 } diff --git a/TangDou/AcWing/MinimalPath/920.cpp b/TangDou/AcWing/MinimalPath/920.cpp index 5d23372..d464d4d 100644 --- a/TangDou/AcWing/MinimalPath/920.cpp +++ b/TangDou/AcWing/MinimalPath/920.cpp @@ -4,23 +4,23 @@ using namespace std; const int INF = 0x3f3f3f3f; typedef pair PII; -const int N = 1e5 + 10; -const int M = 2 * N; +const int N = 1e5 + 10, M = N << 1; int n; // 总共有N个车站 int m; // 开通了M条单程巴士线路 int h[N], e[M], w[M], ne[M], idx; -int dist[N]; // 最小距离数组 +int dis[N]; // 最小距离数组 +bool st[N]; // 是否在队列中 + int stop[N]; // 站点数组 -bool st[N]; // 是否在队列中 void add(int a, int b, int c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++; } // 求1号点到n号点的最短路距离,如果从1号点无法走到n号点则返回-1 void dijkstra() { - memset(dist, 0x3f, sizeof dist); // 求最小设最大 - dist[1] = 0; // 1到自己,乘车数0 + memset(dis, 0x3f, sizeof dis); // 求最小设最大 + dis[1] = 0; // 1到自己,乘车数0 priority_queue, greater> q; // 小顶堆 q.push({0, 1}); // 1号入队列 @@ -28,14 +28,13 @@ void dijkstra() { auto t = q.top(); q.pop(); int u = t.second; - int d = t.first; // 此处 d=t.first没有用上,经测试,其实d=dist[u],用哪个都是一样的 if (st[u]) continue; st[u] = true; for (int i = h[u]; ~i; i = ne[i]) { int v = e[i]; - if (dist[v] > dist[u] + w[i]) { - dist[v] = dist[u] + w[i]; - q.push({dist[v], v}); + if (dis[v] > dis[u] + w[i]) { + dis[v] = dis[u] + w[i]; + q.push({dis[v], v}); } } } @@ -43,16 +42,16 @@ void dijkstra() { int main() { memset(h, -1, sizeof h); // 初始化邻接表 - scanf("%d%d", &m, &n); // 总共有N个车站,开通了M条单程巴士线路 + cin >> m >> n; // 总共有N个车站,开通了M条单程巴士线路 while (m--) { // m条边 // ① 先读入第一个数字 int cnt = 0; // cnt一定要清零 - scanf("%d", &stop[++cnt]); + cin >> stop[++cnt]; char ch = getchar(); while (ch == ' ') { // ② 读入其它数字 - scanf("%d", &stop[++cnt]); // 还有就继续读 - ch = getchar(); // 为下一次做准备 + cin >> stop[++cnt]; // 还有就继续读 + ch = getchar(); // 为下一次做准备 } // 这个建图建的妙啊! // 通过多条边,成功映射了问题,将一趟车问题转化为多个点之间边是1问题 @@ -62,9 +61,9 @@ int main() { } dijkstra(); - if (dist[n] == INF) + if (dis[n] == INF) puts("NO"); else - printf("%d\n", dist[n] - 1); + printf("%d\n", dis[n] - 1); return 0; } \ No newline at end of file diff --git a/TangDou/AcWing/MinimalPath/920.md b/TangDou/AcWing/MinimalPath/920.md index 9074f60..9cab593 100644 --- a/TangDou/AcWing/MinimalPath/920.md +++ b/TangDou/AcWing/MinimalPath/920.md @@ -59,23 +59,23 @@ using namespace std; const int INF = 0x3f3f3f3f; typedef pair PII; -const int N = 1e5 + 10; -const int M = 2 * N; +const int N = 1e5 + 10, M = N << 1; int n; // 总共有N个车站 int m; // 开通了M条单程巴士线路 int h[N], e[M], w[M], ne[M], idx; -int dist[N]; // 最小距离数组 +int dis[N]; // 最小距离数组 +bool st[N]; // 是否在队列中 + int stop[N]; // 站点数组 -bool st[N]; // 是否在队列中 void add(int a, int b, int c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++; } // 求1号点到n号点的最短路距离,如果从1号点无法走到n号点则返回-1 void dijkstra() { - memset(dist, 0x3f, sizeof dist); // 求最小设最大 - dist[1] = 0; // 1到自己,乘车数0 + memset(dis, 0x3f, sizeof dis); // 求最小设最大 + dis[1] = 0; // 1到自己,乘车数0 priority_queue, greater> q; // 小顶堆 q.push({0, 1}); // 1号入队列 @@ -83,14 +83,13 @@ void dijkstra() { auto t = q.top(); q.pop(); int u = t.second; - int d = t.first; // 此处 d=t.first没有用上,经测试,其实d=dist[u],用哪个都是一样的 if (st[u]) continue; st[u] = true; for (int i = h[u]; ~i; i = ne[i]) { int v = e[i]; - if (dist[v] > dist[u] + w[i]) { - dist[v] = dist[u] + w[i]; - q.push({dist[v], v}); + if (dis[v] > dis[u] + w[i]) { + dis[v] = dis[u] + w[i]; + q.push({dis[v], v}); } } } @@ -98,16 +97,16 @@ void dijkstra() { int main() { memset(h, -1, sizeof h); // 初始化邻接表 - scanf("%d%d", &m, &n); // 总共有N个车站,开通了M条单程巴士线路 + cin >> m >> n; // 总共有N个车站,开通了M条单程巴士线路 while (m--) { // m条边 // ① 先读入第一个数字 int cnt = 0; // cnt一定要清零 - scanf("%d", &stop[++cnt]); + cin >> stop[++cnt]; char ch = getchar(); while (ch == ' ') { // ② 读入其它数字 - scanf("%d", &stop[++cnt]); // 还有就继续读 - ch = getchar(); // 为下一次做准备 + cin >> stop[++cnt]; // 还有就继续读 + ch = getchar(); // 为下一次做准备 } // 这个建图建的妙啊! // 通过多条边,成功映射了问题,将一趟车问题转化为多个点之间边是1问题 @@ -117,10 +116,10 @@ int main() { } dijkstra(); - if (dist[n] == INF) + if (dis[n] == INF) puts("NO"); else - printf("%d\n", dist[n] - 1); + printf("%d\n", dis[n] - 1); return 0; } ``` diff --git a/TangDou/Topic/【最短路径】Dijkstra算法专题.md b/TangDou/Topic/【最短路径】Dijkstra算法专题.md index 2937911..d346cba 100644 --- a/TangDou/Topic/【最短路径】Dijkstra算法专题.md +++ b/TangDou/Topic/【最短路径】Dijkstra算法专题.md @@ -81,6 +81,28 @@ int main() { ### [$AcWing$ $1129$. 热浪](https://www.acwing.com/problem/content/description/1131/) 与模板相比,只是起点和终点是输入的,其它无区别。 +**输入样例**: +```cpp {.line-numbers} +7 11 5 4 +2 4 2 +1 4 3 +7 2 2 +3 4 3 +5 7 5 +7 3 3 +6 1 1 +6 3 4 +2 4 3 +5 6 3 +7 2 1 +``` + +**输出样例**: +```cpp {.line-numbers} +7 +``` + + **$Code$** ```cpp {.line-numbers} #include @@ -140,6 +162,20 @@ int main() { #### [$AcWing$ $1128$. 信使](https://www.acwing.com/problem/content/1130/) **总结**:从$1$号哨所出发,计算出到每个哨所的最短路径,所以最短路径中最长的,表示需要的最少时间,是一个最短路径模板+思维问题。 +**输入样例**: +```cpp {.line-numbers} +4 4 +1 2 4 +2 3 7 +2 4 1 +3 4 6 +``` + +**输出样例**: +```cpp {.line-numbers} +11 +``` + **$Code$** ```cpp {.line-numbers} @@ -199,4 +235,261 @@ int main() { return 0; } +``` + +#### [$AcWing$ $1127$. 香甜的黄油](https://www.acwing.com/problem/content/1129/) + +**总结**:本题不是有固定的起点和终点,是起点不一定是哪个。我们需要枚举每一个点做为起点,然后计算每个点作为起点时,消耗的总的边权和,也是代价值。最后比较一下最小的代价值,可以找出哪个点作为起点是最好的选择。 + +**输入样例**: +```cpp {.line-numbers} +3 4 5 +2 +3 +4 +1 2 1 +1 3 5 +2 3 7 +2 4 3 +3 4 5 +``` + +**输出样例**: +```cpp {.line-numbers} +8 +``` + +**$Code$** +```cpp {.line-numbers} +#include + +using namespace std; +typedef pair PII; +const int N = 810; // 牧场数 上限800 +const int M = 3000; // 牧场间道路数 上限1450,无向图开双倍 +const int INF = 0x3f3f3f3f; +// 邻接表 +int h[N], e[M], w[M], ne[M], idx; +void add(int a, int b, int c) { + e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++; +} + +int n, p, m; // 三个数:奶牛数 ,牧场数 ,牧场间道路数 +int id[N]; // 每只奶牛在哪个牧场 +int dis[N]; // 记录起点到任意点的最短路径 +bool st[N]; // 标识每个牧场是否入过队列 + +int dijkstra(int S) { + memset(st, 0, sizeof st); + memset(dis, 0x3f, sizeof dis); + dis[S] = 0; + priority_queue, greater> q; + q.push({0, S}); + + while (q.size()) { + PII t = q.top(); + q.pop(); + + int u = t.second; + if (st[u]) continue; + st[u] = true; + + for (int i = h[u]; ~i; i = ne[i]) { + int v = e[i]; + if (dis[v] > dis[u] + w[i]) { + dis[v] = dis[u] + w[i]; + q.push({dis[v], v}); + } + } + } + int res = 0; + for (int i = 1; i <= n; i++) { // 遍历每只奶牛 + int j = id[i]; // j号牧场 + if (dis[j] == INF) return INF; // 如果j号牧场失联了,则无法获得结果 + res += dis[j]; // 累加一个最小距离 + } + return res; // 整体的最小距离 +} +int main() { + memset(h, -1, sizeof h); + cin >> n >> p >> m; // 奶牛数,牧场数,牧场间道路数 + for (int i = 1; i <= n; i++) cin >> id[i]; // 1 到 N 头奶牛所在的牧场号 + + while (m--) { + int a, b, c; + cin >> a >> b >> c; + add(a, b, c), add(b, a, c); + } + int ans = INF; + + // 枚举每个牧场为出发点,计算它的最短距离和 中的最小值 + for (int i = 1; i <= p; i++) ans = min(ans, dijkstra(i)); + + printf("%d\n", ans); + return 0; +} +``` + +#### [$AcWing$ $1126$. 最小花费](https://www.acwing.com/problem/content/1128/) + +假设初始金钱为$N$,那么如果要在最后一个人的手里得到$100$元,可得公式: +$$\large N∗(1−z_1\%)∗(1−z_2\%)∗…∗(1−z_n\%)=100$$ + +$\Rightarrow$ +$$\large N=\frac{100}{(1−z_1\%)∗(1−z_2\%)∗…∗(1−z_n\%)}$$ + +要想$N$尽可能小,那么就要让 **分母尽可能大** ,即求$(1−z_1\%)∗(1−z_2\%)∗…∗(1−z_n\%)$的最大值。 + +**输入样例**: +```cpp {.line-numbers} +3 3 +1 2 1 +2 3 2 +1 3 3 +1 3 +``` + +**输出样例**: +```cpp {.line-numbers} +103.07153164 +``` + +**$Code$** +```cpp {.line-numbers} +#include +using namespace std; + +const int N = 2010; +const int M = 2e5 + 10; + +typedef pair PDI; // 堆中数值是浮点数,注意区别 + +int n, m; +double dis[N]; +bool st[N]; + +int h[N], e[M], ne[M], idx; +double w[M]; // 边权为浮点数,与一般的题目有区别 +void add(int a, int b, double c) { + e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++; +} +int S, T; +void dijkstra() { + priority_queue q; // 大顶堆 + dis[S] = 1; + q.push({1, S}); + + while (q.size()) { + auto t = q.top(); + q.pop(); + int u = t.second; + if (st[u]) continue; + st[u] = true; + + for (int i = h[u]; ~i; i = ne[i]) { + int v = e[i]; + double a = 1 - w[i]; + if (dis[v] < dis[u] * a) { + dis[v] = dis[u] * a; + q.push({dis[v], v}); + } + } + } +} + +int main() { + memset(h, -1, sizeof h); + cin >> n >> m; + + while (m--) { + int a, b, c; + cin >> a >> b >> c; + double w = c * 0.01; + add(a, b, w), add(b, a, w); + } + + cin >> S >> T; + + dijkstra(); + printf("%.8lf\n", 100 / dis[T]); + return 0; +} +``` + +#### [$AcWing$ $920$. 最优乘车](https://www.acwing.com/problem/content/922/) + +**总结**: +① 建图是本题的关键!同一趟车,不管走几站,走多远,花多少钱,都算是同一趟车,边权都是$1$! +② 本题的输入也是一大特点,每趟车不知道具体有几站,只知道换行算结束,需要学习读入办法。 + +```cpp {.line-numbers} +#include + +using namespace std; +const int INF = 0x3f3f3f3f; +typedef pair PII; + +const int N = 1e5 + 10, M = N << 1; + +int n; // 总共有N个车站 +int m; // 开通了M条单程巴士线路 +int h[N], e[M], w[M], ne[M], idx; +int dis[N]; // 最小距离数组 +bool st[N]; // 是否在队列中 + +int stop[N]; // 站点数组 + +void add(int a, int b, int c) { + e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++; +} +// 求1号点到n号点的最短路距离,如果从1号点无法走到n号点则返回-1 +void dijkstra() { + memset(dis, 0x3f, sizeof dis); // 求最小设最大 + dis[1] = 0; // 1到自己,乘车数0 + priority_queue, greater> q; // 小顶堆 + q.push({0, 1}); // 1号入队列 + + while (q.size()) { + auto t = q.top(); + q.pop(); + int u = t.second; + if (st[u]) continue; + st[u] = true; + for (int i = h[u]; ~i; i = ne[i]) { + int v = e[i]; + if (dis[v] > dis[u] + w[i]) { + dis[v] = dis[u] + w[i]; + q.push({dis[v], v}); + } + } + } +} + +int main() { + memset(h, -1, sizeof h); // 初始化邻接表 + cin >> m >> n; // 总共有N个车站,开通了M条单程巴士线路 + while (m--) { // m条边 + // ① 先读入第一个数字 + int cnt = 0; // cnt一定要清零 + cin >> stop[++cnt]; + char ch = getchar(); + while (ch == ' ') { + // ② 读入其它数字 + cin >> stop[++cnt]; // 还有就继续读 + ch = getchar(); // 为下一次做准备 + } + // 这个建图建的妙啊! + // 通过多条边,成功映射了问题,将一趟车问题转化为多个点之间边是1问题 + for (int i = 1; i <= cnt; i++) + for (int j = i + 1; j <= cnt; j++) + add(stop[i], stop[j], 1); + } + + dijkstra(); + if (dis[n] == INF) + puts("NO"); + else + printf("%d\n", dis[n] - 1); + return 0; +} ``` \ No newline at end of file