题意:给n个点的点权以及m条有向边,求图中一个 点权和与边权和之比 最大的环的 点权和与边权和之比。
思路:与环有关的可以想到SPFA,
设点权为c,i到j的边权为w[i][j],
设答案为ans,点1~p依次连成所求环, 则,
(c[1] + c[2] + ... + c[p]) / (w[1][2] + w[2][3] + ... + w[p][1]) = ans;
ans不好直接求,考虑二分答案,若当前判断是否存在 点权和与边权和之比 > t,
则重新定义从 i 到 j 边权为: w[i][j] * t - c[j],
此时若存在负权环,则考虑这个环的边权之和,
(w[1][2] + w[2][3]... + w[p][1]) * t - (c[1] ... + c[p]) < 0
t < (c[1] ... + c[p]) / (w[1][2] + w[2][3] ... + w[p][1]) = ans, 即答案一定比 t 大,
若不存在反之。
注意最好用%f输出。。。
代码:
#include <cstdio>
#include <iostream>
#include <cmath>
#include <queue>
#include <cstring>
#include <vector>
#define For(i,j,k) for(int i = j;i <= (int)k;i ++)
#define Set(i,j) memset(i, j, sizeof(i))
#define pb push_back
using namespace std;
const int N = 1010;
vector<int> G[N], w[N];
int n, m, c[N], inq[N];
double d[N];
void Add(int x, int y, int z){
G[x].pb(y), w[x].pb(z);
}
bool SPFA(double val){
queue<int> q;
For(i,0,n) d[i] = 1e18;
Set(inq, 0);
inq[1] = -1, d[1] = 0, q.push(1);
while(!q.empty()){
int s = q.front(); q.pop();
inq[s] = -inq[s];
For(i,0,G[s].size() - 1){
int v = G[s][i];
double dis = val * w[s][i] - c[v];
if(dis + d[s] < d[v]){
d[v] = dis + d[s];
if(inq[v] >= 0){
if(inq[v] >= n) return 1;
inq[v] = -inq[v] - 1;
q.push(v);
}
}
}
}
return 0;
}
int main(){
scanf("%d%d", &n, &m);
For(i,1,n) scanf("%d", &c[i]);
For(i,1,m){
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
Add(x, y, z);
}
double L = 0, R = N, M;
while(R - L > 1e-4){
M = (L + R) / 2;
if(SPFA(M)) L = M;
else R = M;
}
printf("%.2f", M);
return 0;
}