TZOJ:6544
题目大意是要求一个最小值 μ,定义为图中环的(边权值之和)/(该环内的点数)。
分式规划问题一般都是二分,所以先把二分模板写出来,然后开始思考怎么判断该二分答案x是否可行。
首先可以得到这个公式
属于这个环的边权值之和除以这个环内的点数要小于等于当前二分答案x。
化简得到:
由于权值的累加要减掉x*k,实际上就是每个边权值都减去k,就得到了
所以,Wi-x就可以作为他的权值使用,那么我们只需要发现至少一个负环,就说明当前二分答案是可行的。
求负环的方法,需要用的bellman(spfa)算法,用于判断图内有无负环产生,并求出起点到各个点的最小值。
其中dfs就是bellman算法,存储也要用稀疏图去存储,遍历每一条边的情况,如果出现更短的路,就要继续dfs下去,直到没有更短路被更新或者被更新点已经走过,就说明有最短路产生。
#include<bits/stdc++.h>
using namespace std;
#define double long double
const int N=3e3+10;
int n,m,bj[N];
double a[N];
struct zuo
{
int y;
double z;
}p;
vector<struct zuo>s[N];
int dfs(int x,double t)
{
bj[x]=1;
for(int i=0;i<s[x].size();i++)
{
if(a[s[x][i].y]>a[x]+s[x][i].z-t)
{
a[s[x][i].y]=a[x]+s[x][i].z-t;
if(bj[s[x][i].y]||dfs(s[x][i].y,t))return 1;
}
}
bj[x]=0;
return 0;
}
int check(double x)
{
memset(bj,0,sizeof(bj));
for(int i=1;i<=n;i++)a[i]=1e9;
for(int i=1;i<=n;i++)
if(dfs(i,x))return 1;
return 0;
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int x;
cin>>x>>p.y>>p.z;
s[x].push_back(p);
}
double l=-1e7,r=1e7;
while(r-l>1e-10)
{
double mid=(l+r)/2;
if(check(mid))r=mid;
else l=mid;
}
cout<<setiosflags(ios::fixed);
cout<<setprecision(8)<<r+1e-10<<endl;
}