http://poj.org/problem?id=3621
全文翻译参自洛谷:https://www.luogu.org/problemnew/show/P2868
题目大意:一个有向图,每个点都有一个价值,每条路通过需要一定时间,求出一个回路使得价值和/时间和最大。(重复经过一个点不会额外增加价值)
按照01分数规划的套路,我们显然可以将路的边权更改为时间*枚举的答案-目的地价值,然后找一个环。
如果这个环是一个负环,那么显然答案还可以变得更大,反之则需要变小。
所以我们需要用spfa判断图中的负环。但是bfs-spfa显然太慢,所以我们需要更高效的算法——dfs-spfa,需要优化的地方。
1.由于我们的目的不是为了求最短路,所以大可以不必将dis全部清成inf(其实连清空都不需要,可以感性理解)。
2.dfs-spfa的好处在于一直搜,直到搜到我们找到的点已经被找过为止。
3.剩下的就是spfa的基本操作了——当dis被更新的时候才能走这个点。
(double和int傻傻搞不清楚……)
#include<cstdio> #include<cmath> #include<cstring> #include<queue> #include<cctype> #include<algorithm> using namespace std; typedef double dl; const dl eps=1e-7; const int INF=2147483647; const int M=5001; const int N=1001; inline int read(){ int X=0,w=0;char ch=0; while(!isdigit(ch)){w|=ch=='-';ch=getchar();} while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar(); return w?-X:X; } struct node{ int to,nxt,l; }e[M]; int head[N],f[N],cnt,n,m; bool vis[N]; dl dis[N],w[M]; inline void add(int u,int v,int l){ cnt++; e[cnt].to=v; e[cnt].l=l; e[cnt].nxt=head[u]; head[u]=cnt; return; } bool spfa(int u){ vis[u]=1; for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to; if(dis[v]>dis[u]+w[i]){ dis[v]=dis[u]+w[i]; if(vis[v]||spfa(v)){ vis[v]=0; return 1; } } } vis[u]=0; return 0; } bool pan(){ for(int i=1;i<=n;i++){ if(spfa(i))return 1; } return 0; } int main(){ n=read();m=read(); for(int i=1;i<=n;i++)f[i]=read(); for(int i=1;i<=m;i++){ int u=read(),v=read(),l=read(); add(u,v,l); } dl l=0,r=100000; while(r-l>eps){ dl mid=(l+r)/2; for(int i=1;i<=cnt;i++){ int v=e[i].to; w[i]=(dl)mid*e[i].l-f[v]; } if(pan())l=mid; else r=mid; } printf("%.2f ",l); return 0; }