给一个有向图,奶牛每次从图中任意一个点出发回到该点(至少经过两个点)。问sigma(点权)/sigma(边权)的最大值可以是多少。
首先从起点出发回到起点所经过的路程肯定是一个环。设最大值为ans。那么sigma(点权)/sigma(边权)=ans。
稍微化简上式子可以得到sigma(边权)*ans-sigma(点权)=0。二分答案,如果值小于答案那么以edge[i]*mid-v[i]为边的图必定存在负环。
那么只需要把是否存在负环作为二分判断条件即可。负环可以用dfs(O(nlogn)?)或者bfs(复杂度O(ke),k最大可能为n)两种方式得到。
dfs:
#include <bits/stdc++.h> #define mem(x) memset(x,0,sizeof(x)) #define mem1(x) memset(x,-1,sizeof(x)) using namespace std; typedef double db; const db eps = 1e-5; const int ME = 5e3+7; const int M = 1e3+7; int n,m,cnt,head[M],vis[M]; db dis[M],val[M],mid; struct edge { int v,next;db w; }e[ME]; void init(){ cnt=0;mem1(head); mem(vis);mem(dis);//vis和dis必须初始化为0 } void add(int u,int v,db w){ e[++cnt].v=v;e[cnt].w=w;e[cnt].next=head[u]; head[u]=cnt; } int spfa(int u){ vis[u]=1; for(int i=head[u];~i;i=e[i].next){ int v=e[i].v; if(dis[v]>dis[u]+e[i].w*mid-val[v]){ dis[v]=dis[u]+e[i].w*mid-val[v]; if(vis[v]||spfa(v)){//该节点可以更新而且已经在栈里就有负环 vis[u]=0; return 1; } } } vis[u]=0; return 0; } int check(){ for(int i=1;i<=n;i++)//每个节点进行一次spfa if(spfa(i)) return 1; return 0; } int main(){ init(); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%lf",&val[i]); for(int i=1;i<=m;i++){ int from,to;db v; scanf("%d%d%lf",&from,&to,&v); add(from,to,v); } db l=1,r=1e4; while(r-l>eps){ mid=(l+r)/2; if(check()) l=mid; else r=mid; } printf("%.2f\n",l); return 0; }