分数规划。
二分答案,用输入的权值减去答案。判负环。
需要用dfs判负环,spfa会T。
dfs判负环:
初始dist都为0。只走能更新dist的点。若走到了vis=1的点,则有负环。
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
const int size=1000010;
const double INF=233333333.0;
int head[size],nxt[size],tot=0;
double dist[size];
struct edge{
int t;
double d,dd;
}l[size];
void build(int f,int t,double d)
{
l[++tot].t=t;
l[tot].dd=d;
nxt[tot]=head[f];
head[f]=tot;
}
int n,m,s;
bool vis[size];
bool dfs(int u)
{
for(int i=head[u];i;i=nxt[i])
{
int v=l[i].t;
if(dist[v]>dist[u]+l[i].d)
{
dist[v]=dist[u]+l[i].d;
if(vis[v]) return true;
vis[v]=1;
if(dfs(v)) return true;
vis[v]=0;
}
}
return false;
}
bool check(double ans)
{
for(int i=1;i<=tot;i++) l[i].d=l[i].dd-ans;
// for(int i=1;i<=tot;i++) cout<<l[i].t<<" "<<l[i].d<<" ";puts("");
memset(dist,0,sizeof(dist));
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)
{
if(!vis[i]&&dfs(i)) return true;
}
return false;
}
double div()
{
double l=-INF,r=INF;
for(int i=1;i<=60;i++)
{
double mid=(l+r)/2.0;
if(check(mid)) r=mid;
else l=mid;
// for(int j=1;j<=n;j++) cout<<dist[j]<<" ";puts(""); cout<<mid<<endl;
}
return l;
}
void scanf(int &n)
{
n=0;
int flag=1;
char a=getchar();
while(a>'9'||a<'0') {if(a=='-') flag=-1;a=getchar();}
while('0'<=a&&a<='9')
{
n=(n<<3)+(n<<1)+a-'0';
a=getchar();
}
n*=flag;
}
int main()
{
scanf(n); scanf(m);
for(int i=1;i<=m;i++)
{
int a,b;double c;
scanf(a); scanf(b);
scanf("%lf",&c);
build(a,b,c);
}
printf("%.8lf\n",div());
return 0;
}
/*
4 5
1 2 5
2 3 5
3 1 5
2 4 3
4 1 3
2 2
1 2 -2.9
2 1 -3.1
*/