题意:
给出一个有向图,n个顶点,m条边
再给出a,b,c,代表从顶点a到顶点b的权值是c
要求输出该有向图中平均权值最小的环的权值,这个环有可能不存在
二分二分二分二分为什么总是忘记有这东西。。
那么上界用权值最大的那条边,在输入的时候记录一下max_edge就可以了。
首先如果枚举值比max_edge还大,但是还是没有负环,那说明是不存在环的
然后就开始二分了,如果有负环,那么这些边权应更大,cost要减掉的应该更少,
没有负环,cost要减掉的更多
直到得到结果~
#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
#define maxx 55
#define maxm 100010
using namespace std;
int first[maxx],v[maxm],next[maxm],visit[maxx],cnt[maxx];
double d[maxx],cost[maxm];
int e,max_edge,n,m;
void add_edge(int a,int b,int c)
{
v[e]=b;
next[e]=first[a];
first[a]=e;
cost[e]=c;
e++;
}
bool spfa()
{
queue<int> q;
memset(visit,0,sizeof(visit));
memset(cnt,0,sizeof(cnt));
for(int i=1;i<=n;i++)
{
q.push(i);
d[i]=0;
}
visit[1]=1;
while(!q.empty())
{
int num=q.front();
q.pop();
visit[num]=0;
for(int i=first[num];i!=-1;i=next[i])
{
if(d[v[i]]>d[num]+cost[i])
{
d[v[i]]=d[num]+cost[i];
if(!visit[v[i]])
{
q.push(v[i]);
visit[v[i]]=1;
if(++cnt[v[i]]>n)return true;
}
}
}
}
return false;
}
bool check(double x)
{
bool ret;
for(int i=0;i<e;i++)
cost[i]-=x;
ret=spfa();
for(int i=0;i<e;i++)//减去的要加回来
cost[i]+=x;
return ret;
}
int main()
{
int N,a,b,c;
scanf("%d",&N);
for(int i=1;i<=N;i++)
{
scanf("%d%d",&n,&m);
e=0;max_edge=0;
memset(first,-1,sizeof(first));
for(int j=0;j<m;j++)
{
scanf("%d%d%d",&a,&b,&c);
max_edge=max_edge>c?max_edge:c;
add_edge(a,b,c);
}
double mid,l=0,r=max_edge;
if(!check(max_edge+1))
{
printf("Case #%d: No cycle found.\n",i);
continue;
}
while(r-l>1e-3)
{
mid=l+(r-l)/2;
if(check(mid))r=mid;//有负环就不应该减掉那么多
else l=mid;
}
printf("Case #%d: %.2f\n",i,l);
}
return 0;
}