HNOI2009 最小圈
题目大意是给定一个有向图,定义环的平均值为环上的边权和/边数,求出最小的环平均值
此题为0-1规划sigma(wi)/x>=L ==> sigma(wi)-x*L>=0 ==>sigma(wi/x)-L>=0
因此二分L,看是否有零环,但零环不好判(L=L*),转换成判负环(L>=L*),存在负环r=mid,否则l=mid
据说spfa会超时,必须dfs搜负环,对每个点每次朝边权值减小方向搜索,直到不能搜索为止,搜索过程中遇到已访问的点,即存在负环。至于为什么这样?貌似不好证明,但是举了大量例子没有出现反例。
代码:
#include<cstdio> #include<iostream> #include<cstring> #define Maxn 10010 #define eps 1e-10 using namespace std; struct line{ int to; double w; line(int t=0,double ww=0):to(t),w(ww){} }p[Maxn],q[Maxn]; int head[Maxn/3],nxt[Maxn],vis[Maxn/3]; double dis[Maxn/3]; int n,m; const double inf=1e7+10; bool dfs(int u){ vis[u]=1; for(int i=head[u];i!=-1;i=nxt[i]){ int v=q[i].to; if(dis[u]+q[i].w<dis[v]){ dis[v]=dis[u]+q[i].w; if(!vis[v]){ dis[v]=dis[u]+q[i].w; if(dfs(v)) return true; } else return true; } } vis[u]=0; return false; } bool check(double mid){ for(int i=1;i<=n;i++) for(int j=head[i];j!=-1;j=nxt[j]) q[j].w=p[j].w-mid; memset(dis,0,sizeof dis); memset(vis,0,sizeof vis); for(int i=1;i<=n;i++) if(dfs(i)) return true; return false; } int main() { int fr,to; double w; while(~scanf("%d%d",&n,&m)){ memset(head,-1,sizeof head); for(int i=0;i<m;i++){ scanf("%d%d%lf",&fr,&to,&w); p[i]=line(to,w); q[i]=line(to,w); nxt[i]=head[fr]; head[fr]=i; } double l=-inf,r=inf; while(r-l>eps){ double mid=(l+r)/2; if(check(mid)) r=mid; else l=mid; } printf("%.8f\n",l); } return 0; }