JZOJ4735【NOIP2016提高A组模拟8.24】最小圈 Spfa深搜判负环

来源:互联网 发布:淘宝专业版一钻以上 编辑:程序博客网 时间:2024/06/14 06:49

题目大意

给你一幅N个点M条边的有向图,要你求图中最小圈的平均值最小是多少,即若一个圈经过k个节点,那么一个圈的平均值为圈上k条边权的和除以k,现要求其中的最小值。

N5000
M10000

解题思路

看到这题一个很好的思路就是二分答案,把问题转化成判定性问题。

二分答案后,将每条边的边权都减去答案Ans,那么问题就转变成了判定一幅图中是否存在负环,一个经典的做法就是用Spfa,判断一个点有没有被加入超过N次,如果有则存在负环,可是这个复杂度是O(NM)的,不能通过本题。

还有一种判负环的思想就是用Dfs来跑Spfa,然后一个点重复出现时就存在负环。具体实现可以一开始把所有点的初始距离设为0,然后枚举以每个点位开头是否存在负环,因为一个负环总有一个位置开始到每个点的路径都是负数。用这种做法就可以通过本题了。

程序

//YxuanwKeith#include <cstring>#include <algorithm>#include <cstdio>using namespace std;const int MAXN = 3e3 + 5, MAXM = 1e4 + 5;const int Inf = 1e5 + 5;const double eps = 1e-10;double Dis[MAXN], Mid, Len[MAXM * 2];int N, M;int tot, Last[MAXN], Next[MAXM * 2], Go[MAXM * 2];bool Flag[MAXN], Ok;void Link(int u, int v, double w) {    Next[++ tot] = Last[u], Last[u] = tot, Go[tot] = v, Len[tot] = w;}void Spfa(int Now) {    Flag[Now] = 1;    for (int p = Last[Now]; p; p = Next[p]) {        int v = Go[p];        if (Dis[Now] + Len[p] - Mid < Dis[v]) {            if (Flag[v]) {                Ok = 1;                return;            }            Dis[v] = Dis[Now] + Len[p] - Mid;            Spfa(v);            if (Ok) return;        }    }    Flag[Now] = 0;}bool Chk() {    memset(Dis, 0, sizeof Dis);    memset(Flag, 0, sizeof Flag);    for (int i = 1; i <= N; i ++) {        Ok = 0;        Spfa(i);        if (Ok) return 1;    }    return 0;}int main() {    scanf("%d%d", &N, &M);    for (int i = 1; i <= M; i ++) {        int u, v;        double w;        scanf("%d%d%lf", &u, &v, &w);        Link(u, v, w);    }    double l = -Inf, r = Inf;    while (r - l > eps) {        Mid = (l + r) / 2;        if (Chk()) r = Mid; else l = Mid;    }    printf("%.6lf", Mid);}
2 0
原创粉丝点击