题意:
L个点,P边的点边带权的有向图,求一个环点权和与边权和比值的最大值。
题解:
01分数规划+判负环
详细看这里
还是套用01分数规划模型,点权为value[i],边权为cost[u],一个环为C,问题要求最大化
最小化就是符号倒过来
和之前一样处理,设当前答案为r,设边权为dis[i] = r * ∑cost[i] - ∑value[i]
如果r < r *,则说明至少存在一个环,d[i] < 0,也就是存在负权回环,边权值并不是提前算好,而是在更新路径的时候从哪个点访问到这个边的就将这条边设为相应点权与边权的对应值
如果r > r * ,则不存在负环
判负环一半用spfa,方法:一个点不能入队n次,否则有负环;一条最短路径长度不能到n,否则有负环。貌似后者更快
代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define N 1005
#define E 5005const double eps=1e-6;
int n,m,x,y;
int tot,point[N],nxt[E],v[E];double c[E];
double val[N],e[E],ans;
double dis[N];
int cnt[N];bool vis[N];
int q[N];void add(int x,int y)
{++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
}
bool spfa()
{memset(dis,0,sizeof(dis));memset(vis,0,sizeof(vis));;memset(cnt,0,sizeof(cnt));int head=0,tail=0;for (int i=1;i<=n;++i){vis[i]=1;++cnt[i];q[(++tail)%n]=i;}while (head!=tail){int now=q[(++head)%n];vis[now]=0;for (int i=point[now];i;i=nxt[i])if (dis[v[i]]<dis[now]+c[i]){dis[v[i]]=dis[now]+c[i];if (!vis[v[i]]){vis[v[i]]=1;++cnt[v[i]];if (cnt[v[i]]>n) return true;q[(++tail)%n]=v[i];}}}return false;
}
bool check(double L)
{for (int i=1;i<=tot;++i)c[i]=val[v[i]]-L*e[i];return spfa();
}
double find()
{double l=0.0,r=1e4,mid,ans=0.0;while (r-l>eps){mid=(l+r)/2.0;if (check(mid)) ans=l=mid;else r=mid;}return ans;
}
int main()
{scanf("%d%d",&n,&m);for (int i=1;i<=n;++i) scanf("%lf",&val[i]);for (int i=1;i<=m;++i){scanf("%d%d%lf",&x,&y,&e[i]);add(x,y);}ans=find();printf("%.2lf\n",ans);
}