您的位置:首页 > 其它

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

2016-08-24 13:28 423 查看

题目大意

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

N≤5000

M≤10000

解题思路

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

二分答案后,将每条边的边权都减去答案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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: