最优比率环:
给定有点权和边权的图,要求找一个环,使环的点权和与边权和的比值最大。
解决方法: 和01分数规划类似,但新权值的式子有所不同。来源:https://blog.youkuaiyun.com/hzoi_ztx/article/details/54898323
此时求最大比率的式子与01规划的式子有所不同(总算式加个负号)。
核心: 取定一个 r 值以后,带入最短路的新的dis[]更新公式,再判断是否存在至少一个负环(找出一个负环即可),存在负环和不存在负环两种情况指示了 r 应如何取下一个值,直到到达指定精度。
#include<bits/stdc++.h>
#define ll long long
#define lf double
#define IOS std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define DEBUG cout<<",here\n";
#define Rep(i,l,r) for(int i=(l);i<=(r);i++)
#define rep(i,l,r) for(int i=(l);i< (r);i++)
#define Rev(i,r,l) for(int i=(r);i>=(l);i--)
#define rev(i,r,l) for(int i=(r);i> (l);i--)
#define MAXN 1005
#define INF 1e16
using namespace std;
inline ll quickPow(ll a,ll b,ll m){ll ans=1;while(b){if(b&1)ans=(a*ans)%m;a=(a*a)%m;b>>=1;}return ans;}
int f[MAXN];
struct SPFA//魔改版SPFA,dis[]更新与传统最短路不同
{
struct To
{
int id;
double v;
};
vector<To>node[MAXN];
int inq[MAXN],inq_cnt[MAXN];
double dis[MAXN];
void init(int n){
for(int i=1;i<=n;i++){dis[i]=INF;inq[i]=0;inq_cnt[i]=0;}
}
void add(int x,int y,double v){
node[x].push_back({y,v});
}
int spfa(int n,int s,double m){//(int n,int start)
queue<int>q;
q.push(s);
inq[s]=1;dis[s]=0;
while(!q.empty()){
int now=q.front();q.pop();
inq[now]=0;
if(inq_cnt[now]>=n){return 0;}//找不出最短路,有负环
for(auto& i:node[now]){
if(dis[now]+m*i.v-f[now]<dis[i.id]){
dis[i.id]=dis[now]+m*i.v-f[now];
if(!inq[i.id]){
q.push(i.id);inq[i.id]=1;
inq_cnt[i.id]++;
}
}
}
}
return 1;//能找到最短路
}
};
SPFA spfa;
int n,m;
const double eps=1e-6;
int main()
{
cin>>n>>m;
spfa.init(n);
Rep(i,1,n){cin>>f[i];}
Rep(i,1,m){
int x,y;double v;cin>>x>>y>>v;
spfa.add(x,y,v);
}
double l=0,r=1000000.0;//不同的问题应该取不同的初始值
while(r-l>eps){
double M=(l+r)/2;
spfa.init(n);
int jug=0;
Rep(i,1,n){
if(spfa.inq_cnt[i]==0){
if(spfa.spfa(n,i,M)==0)jug=1;
}
}
if(jug) l=M;
else r=M;
}
printf("%.2lf\n",r);
return 0;
}