题目描述
考虑带权的有向图G=(V,E)G=(V,E)以及w:E→Rw:E→R,每条边e=(i,j)(i≠j,i∈V,j∈V)e=(i,j)(i≠j,i∈V,j∈V)的权值定义为wi,jwi,j,令n=|V|n=|V|。c=(c1,c2,⋯,ck)(ci∈V)c=(c1,c2,⋯,ck)(ci∈V)是GG中的一个圈当且仅当(1≤i<k)(1≤i<k)和(ck,c1)(ck,c1)都在EE中,这时称为圈cc的长度同时令,并定义圈c=(c1,c2,⋯,ck)c=(c1,c2,⋯,ck)的平均值为μ(c)=∑i=1kwci,ci+1/kμ(c)=∑i=1kwci,ci+1/k,即cc上所有边的权值的平均值。令为GG中所有圈的平均值的最小值。现在的目标是:在给定了一个图G=(V,E)G=(V,E)以及w:E→Rw:E→R之后,请求出GG中所有圈的平均值的最小值μ′(c)=Min(μ(c))μ′(c)=Min(μ(c))
数据保证G=(V,E)G=(V,E)联通。
Solution
听别人说是01分数规划,但是我觉得这只是一个普通的二分。
二分答案,即最小环的平均值。
验证答案只需要把所有的边权都减去答案,之后判断一下有没有负环即可,如果有负环的话,就说明平均值过大(如果一个环上所有的边权都减去答案后,变成了一个负环,就说明这个环的平均值小于现在的二分出来的答案)。
对于下面这份代码,有几点要说明:
1.使用了不合理的常数优化 double l = -1e4, r = 1e4, res;实际上应该是1e7(不过可以估计出不专门卡的话,平均值不会特别大),或者是最大值和最小值(用正确性换时间)。
2.使用了不合理优化的BFS SPFA sum[v] = sum[now] + 1;应该是sum[v]++;,这种方法是我在网上见到的(似乎后来被证明有问题),但正确性还是比较高的(至少我用到现在还没出过错),而且速度很快(用正确性换时间)。
3.在luogu开O2过去了,但速度还是很慢。
4.听说此题不卡DFS版的SPFA,用DFS的话就可以忽视以上两点了(不知道出题人卡BFS,不卡DFS是什么心态)。
5.欢迎来卡。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
const int maxn = 3007;
const int maxm = 10007;
const double esp = 1e-9;
class Map {
private :
struct Node {
int u, v, nex;
double w;
Node (int u = 0, int v = 0, double w = 0) :
u(u),
v(v),
w(w),
nex(0) {}
};
Node edge[maxm * 2];
int head[maxn], cnt, vis[maxn], in[maxn], sum[maxn];
double cost[maxn];
int n;
std :: queue<int> q;
public :
void Init(int x) {
n = x;
}
void AddEdge(int u, int v, double w) {
edge[++cnt] = Node(u, v, w);
edge[cnt].nex = head[u];
head[u] = cnt;
}
bool SPFA(double dis) {
while (!q.empty()) {
q.pop();
}
memset(vis, 0, sizeof(vis));
memset(sum, 0, sizeof(sum));
memset(in, 0, sizeof(in));
for (int i = 1; i <= n; i++) {
cost[i] = 1e9 + 7;
}
cost[1] = 0;
q.push(1);
while (!q.empty()) {
int now = q.front();
q.pop();
in[now] = 0;
for (int i = head[now]; i; i = edge[i].nex) {
int v = edge[i].v;
double w = edge[i].w;
w -= dis;
if (cost[now] + w < cost[v]) {
cost[v] = cost[now] + w;
sum[v] = sum[now] + 1;
if (sum[v] == n) {
return 0;
}
if (!in[v]) {
in[v] = 1;
q.push(v);
}
}
}
}
return 1;
}
};
class Solution {
private :
int n ,m;
Map mp;
double Find() {
double l = -1e4, r = 1e4, res;
while (fabs(l - r) > esp && r >= l) {
double mid = (l + r) / 2; //mid表示答案
if (mp.SPFA(mid)) { //
res = mid;
l = mid + esp;
} else {
r = mid - esp;
}
}
return res;
}
public :
Solution () {
sol.Get();
sol.Solve();
}
void Get() {
scanf("%d %d", &n, &m);
mp.Init(n);
for (int i = 1; i <= m; i++) {
int u, v;
double w;
scanf("%d %d %lf", &u, &v, &w);
mp.AddEdge(u, v, w);
}
}
void Solve() {
printf("%.8lf\n", Find());
}
};
Solution sol;
int main() {}
这辈子都不可能写主函数的!
547

被折叠的 条评论
为什么被折叠?



