给定一张 L 个点、P 条边的有向图,每个点都有一个权值 f[i],每条边都有一个权值 t[i]。
求图中的一个环,使“环上各点的权值之和”除以“环上各边的权值之和”最大。
输出这个最大值。
注意:数据保证至少存在一个环。
输入格式
第一行包含两个整数 L 和 P。
接下来 L 行每行一个整数,表示 f[i]。
再接下来 P 行,每行三个整数 a,b,t[i],表示点 a 和 b 之间存在一条边,边的权值为 t[i]。
输出格式
输出一个数表示结果,保留两位小数。
数据范围
2≤L≤1000,
2≤P≤5000,
1≤f[i],t[i]≤1000
输入样例:
5 7
30
10
10
5
10
1 2 3
2 3 2
3 4 5
3 5 2
4 5 5
5 1 3
5 2 2
输出样例:
6.00
分析: 题解
这里跑的是一个spfa+多元最短路问题,判断负环的时候这道题发现
1.
if(d[v]>d[u]+w)
{
d[v]=d[u]+w;
if(st1[v])continue;
cnt[v]=cnt[u]+1;
if(cnt[v]>=n)return true;
st1[v]=1;
q.push(v);
}
2.
if(d[v]>d[u]+w)
{
d[v]=d[u]+w;
if(st1[v])continue;
cnt[v]++;
if(cnt[v]>=n)return true;
st1[v]=1;
q.push(v);
}
3.
if(d[v]>d[u]+w)
{
d[v]=d[u]+w;
cnt[v]++;
if(cnt[v]>=n)return true;
if(st1[v])continue;
st1[v]=1;
q.push(v);
}
这三种都能ac,但网上判断负环的方法是判断入队次数,也就是第二种,至于其他两种为什么能过,我也很疑惑,第一种貌似也是入队次数?,第三种就是值更新的次数,也可能是这道题数据水了,总之第二种一定是对的,也恳请各位大佬讲一讲~~
更新:第一种也是对的算是当前点的最段路的边数,第三种的反例如下,点4更新了n次,但图中并没有负环
代码如下:
#include <iostream>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
const int N=1010;
struct node{
int v;
double w;
};
vector<node>adj[N];
int st1[N],cnt[N];//入队次数
double f[N],d[N];
int n,p;
bool bfs(double mid)
{
queue<int>q;
for (int i = 1; i <= n; i++)
st1[i] = 1, d[i] = cnt[i] = 0,q.push(i);;
while(q.size())
{
int u=q.front();
st1[u]=0;
q.pop();
for(auto t:adj[u])
{
int v=t.v;
double w=t.w*mid-f[u];
if(d[v]>d[u]+w)
{
d[v]=d[u]+w;
if(st1[v])continue;
cnt[v]++;
if(cnt[v]>=n)return true;
st1[v]=1;
q.push(v);
}
}
}
return false;
}
int main()
{
cin>>n>>p;
for(int i=1;i<=n;i++)cin>>f[i];
while(p--)
{
int a,b;
double w;
cin>>a>>b>>w;
adj[a].push_back({b,w});
}
double r=1e3,l=0;
while(r-l>=1e-7)
{
double mid=(l+r)/2;
if(bfs(mid))l=mid;
else r=mid;
}
printf("%.2f",l);
return 0;
}