可能见得最多的就是最优比例生成树,常用的解决方案就是二分枚举答案。
最优比例生成树一般求解最大值,如果求最小值那么转换成最大值的倒数即可。求最大值的检测条件是使得式子>=0。
最优比例环,一般求解最小值,如果求最大值那么转换成求最小值的倒数。
求最小值的检测条件是图存在负环。
bzoj 1486 最优比例环
在这道题里如果用spfa或许会tle。可用dfs式的spfa判断负环,沿着边权和减少的方向深搜,如果搜到之前访问过的,那么就存在负环。这里是回溯深搜~
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <math.h>
#include <vector>
#include <cstdio>
#include <string>
#include<string.h>
#include <fstream>
#include <iostream>
#include <algorithm>
using namespace std;
#define exp 1e-8
#define INF 0x3f3f3f3f
#define ll __int64 //%I64d
#define mm(a,b) memset(a,b,sizeof(a));
#define for1(a,b) for(int a=1;a<=b;a++)//1---(b)
#define for0(a,b) for(int a=0;a<=b;a++)//0---(b)
void bug(string st="bug")
{cout<<st<<endl;}
template<typename __ll>
inline void READ(__ll &m){
__ll x=0,f=1;char ch=getchar();
while(!(ch>='0'&&ch<='9')){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
m=x*f;
}
template<typename __ll>
inline void read(__ll &m){READ(m);}
template<typename __ll>
inline void read(__ll &m,__ll &a){READ(m);READ(a);}
template<typename __ll>
inline void read(__ll &m,__ll &a,__ll &b){READ(m);READ(a);READ(b);}
const int cnt_edge=100000; //修改啊
const int cnt_v=100000;
int head[cnt_v],cnt;
struct EDGE
{
int u,v,next;
double cost;
}edge[cnt_edge];
int n,m;
int dat[10010][3];
double cost[10010];
bool flag;
double dist[4000];
bool vis[4000];
void addedge(int u,int v,double cost=0)
{
edge[cnt].u=u;
edge[cnt].v=v;
edge[cnt].cost=cost;
edge[cnt].next=head[u];
head[u]=cnt++;
}
void init() 初始化啊
{
flag=0;
cnt=0;
memset(head,-1,sizeof(head));
}
void dfs(int u)
{
if(flag)return ;
vis[u]=1;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(dist[v]>dist[u]+edge[i].cost)
{
if(vis[v])
{
flag=1;
return ;
}
dist[v]=dist[u]+edge[i].cost;
dfs(v);
}
}
vis[u]=0;
}
bool check(double mid)
{
init();
for1(i,m)addedge(dat[i][1],dat[i][2],cost[i]-mid);
mm(dist,0); //判断负环 这样子初始化就可以了
mm(vis,0);
for1(i,n)
{
dfs(i);
if(flag)return 1;
}
return 0;
}
int main()
{
read(n,m);
for1(i,m)read(dat[i][1],dat[i][2]),scanf("%lf",&cost[i]);
double l=-1000000000,r=1000000000;
while(r-l>=exp)
{
double mid=(l+r)/2;
if(check(mid))r=mid;
else l=mid;
}
printf("%.8lf\n",r);
return 0;
}