大致题意
一个有向图,求其最优的一个环,所谓最优就是 这个环上的点权之和比上边权之和的值最大
分析
做了前面两道题后,到了第三道题,就会感觉分数规划的题思路大致都一样,主要在于定义好 a(x)和 b (x)后,怎么和这道题联系在一起
我们来看,一个环上,其顶点个数和边的条数肯定是一样的,那对于任意一条边,我们就强制规定其新权值为 起点的点权 - r * 该边边权,然后在此基础上如果图中存在一个正权环,那么 r 还可以调大
但怎么有效的判断正权环的存在呢???
思考
正确答案:正难则反
判断正权环我还没学过,但负环的话总是会的,用spfa搞一下就可以了。于是我们重新定义新权值为 r * 该边边权-起点的点权
此时如果存在负环,则原图应该存在正环,则 r 调大
代码
还是有一点点的坑
#include<cstdio>
#include<cstring>
#include<queue>
#include<iostream>
#define ll long long
#define N 1004
#define M 5004
using namespace std;
int n,m,f[N];
int nxt[M],head[N],to[M],dis[M],tot,from[M];
double w[M],d[N];
int vis[N],in[N];
void add(int x,int y,int z){
nxt[++tot]=head[x];head[x]=tot;
from[tot]=x;to[tot]=y;dis[tot]=z;
}
bool spfa(double r){
queue<int > q;memset(vis,0,sizeof(vis));
for(int i=1;i<=n;++i) d[i]=1e15;
memset(in,0,sizeof(in));
for(int i=1;i<=tot;++i) w[i]=r*dis[i]-f[from[i]];
vis[1]++;d[1]=0;
q.push(1);in[1]=1;
while(!q.empty()){
int u=q.front();q.pop();in[u]=0;
for(int e=head[u];e;e=nxt[e]){
int v=to[e];
if(d[v]>d[u]+w[e]){
d[v]=d[u]+w[e];
if(!in[v]){ q.push(v);in[v]=1; }
vis[v]++;
if(vis[v]>n) return 1;
}
}
}
return 0;
}
int main(){
scanf("%d%d",&n,&m);
int i,j,k,x,y,z;
for(i=1;i<=n;++i) scanf("%d",&f[i]);
for(i=1;i<=m;++i){
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
}
double l=0.0,r=10000.0;
// L-R < 1e-3这样写就不可以,会死循环,可能是精度的问题吧
while(r-l>1e-3){
double mid=(l+r)/2.0;
if(spfa(mid)) l=mid;
else r=mid;
}
printf("%.2f",l);
return 0;
}