HNOI2009 最小圈
题目链接:戳我
发现了判断负环的真*写法喂所以说原先那个不是标准判断方法吗!!
就是一个简单的01分数规划,然后我们知道\(\sum w \ge k*mid\)
然后这就相当于把所有的边都减去二分出来的mid值,如果有负环就说明这个二分值大于等于答案了。
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<ctime>
#define MAXN 100010
#define eps 1e-9
using namespace std;
int n,m,t;
int head[MAXN],done[MAXN],cnt[MAXN];
double dis[MAXN];
struct Edge{int nxt,to;double dis;}edge[MAXN<<1];
inline void add(int from,int to,double dis)
{
edge[++t].nxt=head[from],edge[t].to=to,edge[t].dis=dis;
head[from]=t;
}
inline bool spfa(int x)
{
done[x]=1;
for(int i=head[x];i;i=edge[i].nxt)
{
int v=edge[i].to;
if(dis[v]>dis[x]+edge[i].dis)
{
dis[v]=dis[x]+edge[i].dis;
if(done[v]||spfa(v)) return true;
}
}
done[x]=0;
return false;
}
inline bool check(double x)
{
for(int i=1;i<=n;i++)
for(int j=head[i];j;j=edge[j].nxt)
edge[j].dis-=x;
for(int i=1;i<=n;i++) dis[i]=0,done[i]=0;
bool flag=false;
for(int i=1;i<=n;i++)
if(spfa(i))
{
flag=true;
break;
}
for(int i=1;i<=n;i++)
for(int j=head[i];j;j=edge[j].nxt)
edge[j].dis+=x;
return flag;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
scanf("%d%d",&n,&m);
double l=1e9,r=-1e9;
for(int i=1;i<=m;i++)
{
int x,y;double w;
scanf("%d%d%lf",&x,&y,&w);
add(x,y,w);
l=min(1.0*w,l),r=max(r,1.0*w);
}
//cout<<l<<" "<<r<<endl;
while(l+eps<r)
{
double mid=(l+r)/2;
// printf("[%.8lf %.8lf] mid=%.8lf\n",l,r,mid);
if(check(mid)) r=mid;
else l=mid;
}
printf("%.8lf\n",l);
return 0;
}