看到网上很多蛋疼的题解,忍不住自己发一份。
问题简单描述:
有图G,节点权值为Fi, 边权值为Ti, 求一回路,使sum(Fi) / sum(Ti) 最大。
变形一下,即求一实数ans,使得:G中存在一回路,回路上 sum(Fi) - sum(Ti)*ans >= 0, 且令这个ans最大。
将 sum(Fi) - sum(Ti)*ans >= 0 变形为 sum( Fi - Ti*ans) >= 0
即把每个边权值变为 Fi - Ti*ans 使得G中存在一个权值大于等于0的回路并让ans最大(事实上ans最大时这个回路权值为0)
存在正权回路的问题我们没有碰到过,但是负权回路可以用spfa或bellmanford解决。
所以把边权值边为 -(Fi - Ti*ans),用spfa判断是否有负权回路来判断ans是否可行。二分查找ans,并把误差控制到0.001。
CODE:
#include <iostream>
#include <queue>
using namespace std;
#define INF 0x3f3f3f3f
struct EDGE{
void set(int a, int b, int t){
u=a; v=b; time=t;
}
int u, v, time;
};
EDGE E[10000];
int head[10000], next[10000], top;
int F[1001], n, m;
void InitGraph(){top=-1; memset(head,-1,sizeof(head));}
void AddEdge(int s, int t, int time){
E[++top].set(s, t, time);
next[top] = head[s];
head[s] = top;
}
bool SPFA(double ans){
queue<int> q;
double dis[1001];
bool inq[1001] = {false};
int cnt[1001] = {0};
for (int i=1; i<=n; i++) dis[i] = 1e100;
dis[1] = 0;
inq[1] = true;
cnt[1] = 1;
q.push(1);
while (!q.empty()){
int u = q.front(); q.pop();
inq[u]= false;
for (int i=head[u]; i!=-1; i=next[i]){
int v=E[i].v, t=E[i].time;
if (dis[v] > dis[u]+(-F[v]+ans*t)){
dis[v] = dis[u]+(-F[v]+ans*t);
if (!inq[v]){
if (++cnt[v] >= n) return true;//有负权环
inq[v] = true;
q.push(v);
}
}
}
}
return false;
}
int main(void)
{
InitGraph();
cin>>n>>m;
for (int i=1; i<=n; i++) scanf("%d", &F[i]);
for (int i=0; i<m; i++){
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
AddEdge(a, b, c);
}
double beg=0, end=20000;
while (end-beg > 0.001){
double mid = (beg+end)/2.0;
if (SPFA(mid)) beg=mid;
else end=mid;
}
if (beg > 0.001) printf("%.2f\n", beg);
else printf("0\n");
return 0;
}