From 155c1823335f16df3400693012693d2da66613ee Mon Sep 17 00:00:00 2001 From: hanyixuanten <105723997+hanyixuanten@users.noreply.github.com> Date: Mon, 28 Sep 2026 20:46:48 +0800 Subject: [PATCH] new file: P3199.cpp --- P3199.cpp | 58 ++++++++++++++++++++++++++++++++++ solutions/P3199.md | 79 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 P3199.cpp create mode 100644 solutions/P3199.md diff --git a/P3199.cpp b/P3199.cpp new file mode 100644 index 0000000..fd541f4 --- /dev/null +++ b/P3199.cpp @@ -0,0 +1,58 @@ +#include +using namespace std; +int n, m; +vector> fri[3005]; +int q[30000005]; +double d[3005]; +bool in[3005]; +int cnt[3005]; +int ql = 1, qr = 0; +bool check(double minus) +{ + ql = 1, qr = 0; + memset(d, 0, sizeof d); + memset(cnt, 0, sizeof cnt); + for (int i = 1; i <= n; ++i) + { + q[++qr] = i; + in[i] = 1; + } + while (qr >= ql) + { + int now = q[ql++]; + in[now] = 0; + for (pair to : fri[now]) + { + if (d[now] + to.second - minus < d[to.first]) + { + d[to.first] = d[now] + to.second - minus, cnt[to.first]++; + if (!in[to.first]) + q[++qr] = to.first, in[to.first] = 1; + } + if (cnt[to.first] > n) + return 1; + } + } + return 0; +} +int main() +{ + scanf("%d%d", &n, &m); + for (int i = 1, u, v; i <= m; ++i) + { + double w; + scanf("%d%d%lf", &u, &v, &w); + fri[u].push_back({v, w}); + } + double l = -1e7, r = 1e7; + while (r - l > 1e-9) + { + double mid = (l + r) / 2; + if (check(mid)) + r = mid; + else + l = mid; + } + printf("%.8lf\n", l); + return 0; +} \ No newline at end of file diff --git a/solutions/P3199.md b/solutions/P3199.md new file mode 100644 index 0000000..14f77d8 --- /dev/null +++ b/solutions/P3199.md @@ -0,0 +1,79 @@ +# P3199 最小圈 + +思路:找到最小圈相当于找到最小环。 + +只须二分答案环的平均值的最小值 $mid$,并把每条边均减去 $mid$,使用 SPFA 判断负环即可。 + +调试记录: + +- 二分范围错误 + - 因为存在负边权,二分范围应该为 $-10^7$ 到 $10^7$ +- SPFA 判负环逻辑错误 + - SPFA 判断负环用的是单个路径的长度 $> n$ 而非访问的点个数 $> n$ +- 初始入队点错误 + - 存在圈且有一个点能到达其他所有点,不一定是点 $1$ +- 入队条件错误 + - SPFA 入队的条件是未在队列中且进行了松弛操作 +- 队列长度不足 + - 一个点可能多次入队,队列长度应该为 $n \times m$ + +```cpp +#include +using namespace std; +int n, m; +vector> fri[3005]; +int q[30000005]; +double d[3005]; +bool in[3005]; +int cnt[3005]; +int ql = 1, qr = 0; +bool check(double minus) +{ + ql = 1, qr = 0; + memset(d, 0, sizeof d); + memset(cnt, 0, sizeof cnt); + for (int i = 1; i <= n; ++i) + { + q[++qr] = i; + in[i] = 1; + } + while (qr >= ql) + { + int now = q[ql++]; + in[now] = 0; + for (pair to : fri[now]) + { + if (d[now] + to.second - minus < d[to.first]) + { + d[to.first] = d[now] + to.second - minus, cnt[to.first]++; + if (!in[to.first]) + q[++qr] = to.first, in[to.first] = 1; + } + if (cnt[to.first] > n) + return 1; + } + } + return 0; +} +int main() +{ + scanf("%d%d", &n, &m); + for (int i = 1, u, v; i <= m; ++i) + { + double w; + scanf("%d%d%lf", &u, &v, &w); + fri[u].push_back({v, w}); + } + double l = -1e7, r = 1e7; + while (r - l > 1e-9) + { + double mid = (l + r) / 2; + if (check(mid)) + r = mid; + else + l = mid; + } + printf("%.8lf\n", l); + return 0; +} +```