题目链接:http://poj.org/problem?id=3621
题意:求一个环路,欢乐值 / 总路径最大(欢乐值是每个点权,边有边权)
思路:ans= val[i]/ w[i] (假设val[i]为点权,w[i]为边权)
ans*w[i]-val[i] = 0 当我们要找最大的ans时,且要保留精度很明显要用二分,寻找最大ans。
哪怎么判定有环呢? 我们知道 spaf是可以判断是否有负环的。
我们将spfa以前的边权改为 ans*w[i]-val[i] 如果有负环则说明,ans偏小了(很明显,有负环表明环内ans*w[i]-val[i]之和<0,要增大ans才能使之和等于0),否则ans偏大。
交到G++wa了一发, 在G++环境下,对于double 类型 真正的标准是用%lf输入,用%f输出。交c++就不用,因为 对于double 类型用%lf 输入用%lf输出是window 环境下VC的标准但不是真正的标准。
代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<queue> #define inf 0x3f3f3f3f using namespace std; typedef long long ll; const int maxn=1500; const int maxm=5500; struct node{ int u,v,w,nxt; }e[maxm]; int head[maxn],val[maxn],num[maxn]; bool vis[maxn]; double d[maxn]; int n,m,cnt=0; void add(int u,int v,int w) { e[cnt].u=u; e[cnt].v=v; e[cnt].w=w; e[cnt].nxt=head[u]; head[u]=cnt++; } bool spfa(double x) { memset(vis,0,sizeof(vis)); memset(num,0,sizeof(num)); queue<int> q; for(int i=1;i<=n;i++) d[i]=inf; d[1]=0; vis[1]=1; q.push(1); num[1]=1; while(!q.empty()) { int u=q.front();q.pop(); vis[u]=0; for(int i=head[u];i!=-1;i=e[i].nxt) { int v=e[i].v; int w=e[i].w; double sum=-val[u]+x*w; if(d[v]>d[u]+sum) { d[v]=d[u]+sum; if(!vis[v]) { vis[v]=1; q.push(v); num[v]++; if(num[v]>n) return true; } } } } return false; } int main() { cnt=0; int u,v,w; memset(head,-1,sizeof(head)); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&val[i]); for(int i=0;i<m;i++) { scanf("%d%d%d",&u,&v,&w); add(u,v,w); } double l=0,r=10000,mid; while(r-l>0.001) { mid=(r+l)/2; if(spfa(mid)) l=mid; else r=mid; } printf("%.2lf\n",l); return 0; }