这道题使我彻底弄明白了spfa为什么最多是n-1个点入队,多一个点入队,就证明是有负环必然,举个栗子
比如说读边的顺序呢是这样的1——5,1——2,2——5,2——3,3——5,3——4,4——5,那么1号点先入队列,然后是5号,和2号入队列,1号出队列,5号点出队列,现在队列里只剩2号点,然后把5号和3号入队,5号出队列,然后把与3号相连的且此时不在队列的5,4入队列,3号出队列,然后5号出队列,然后再把与4号相连的5号入队列,这样,5号点进入了4次队列,而且在没有负环的图里最多只能进n-1次队列,即其他的n-1个点都与它相连,并且当时它并未在队列
下面让我们来探讨一下这个题首先这是一道找环的问题,那么我们可以,把所有边都减去一个值,如果出现了负环的情况,则证明,这是一个环,实际上我们减去的这个值,就是我们要求得平均值,这个题用了二分(我也是看了网上的题解才知道用二分的,没办法实在是太菜了),其实这道题用bellman-ford,和floyd找最小环应该都能做,因为只有50个点,另外既然用spfa做了,就需要考虑这个算法所需要考虑的情况,首先就是可能出现1与2相连,3与4相连,4与3相连,正常情况下,我们是找1为起点,然后开始找最短路即可,但是这道题目是找环,那么就会存在不是连通图的情况,实际上最短路也存在,但是不需要考虑,为什么呢,因为给了起点,找起点到其他地方的距离,到不了的根本不用考虑啊。对吧,所以这种情况下,就有两种选择,第一,刚开始把所有点都入队列,过倒是可以过,但是我并没找到初始化的方法,第二种,也就是我现在代码的这种,就是在减去一个值的同时,把所有点扫一遍,看是否能成环,如果可以,则继续进行
还有一点需要考虑的就是减掉的值在spfa找过之后,一定要再给他加回来,要不然原来的数据就被破坏了,无法进行下一组,最后,别忘了double
#include <iostream>
#include <stdio.h>
#include <queue>
#include <string.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=100;
struct Edgenode
{
int to,next;
double w;
};
Edgenode edges[maxn*maxn];
int n,m;
int head[maxn],cnt[maxn],edge;
bool vis[maxn];
double dis[maxn];
void add(int u,int v,double c)
{
edges[edge].w=c;
edges[edge].to=v;
edges[edge].next=head[u];
head[u]=edge++;
}
void init()
{
memset(head,-1,sizeof(head));
edge=0;
}
bool spfa(int y)
{
queue<int>que;
int u;
for(int i=0; i<=n; i++)
{
dis[i]=INF;
cnt[i]=0;
//vis[i]=0;
}
dis[y]=0;
que.push(y);
memset(vis,0,sizeof(vis));
vis[y]=1;
while(!que.empty())
{
u=que.front();
que.pop();
vis[u]=false;
for(int i=head[u]; i!=-1; i=edges[i].next)
{
int v=edges[i].to;
double w=edges[i].w;
if(dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
if(!vis[v])
{
vis[v]=true;
que.push(v);
if(++cnt[v]>=n)
return 0;
}
}
}
}
return 1;
}
bool check(double x)
{
bool flag=0;
for(int i=1; i<=n; i++)
for(int k=head[i]; k!=-1; k=edges[k].next)
edges[k].w-=x;
for(int i=1; i<=n; i++)
{
if(!spfa(i))//这块是把所有点找一遍,看看能否找到环
flag=1;
}
for(int i=1; i<=n; i++)
for(int k=head[i]; k!=-1; k=edges[k].next)
{
edges[k].w+=x;
}
return flag;
}
int main()
{
int t,case2=1;
int a,b;
double w,l=INF*1.0,r=0.0,mid=0.0;
cin>>t;
while(t--)
{
init();
//cin>>n>>m;
scanf("%d%d",&n,&m);
for(int i=1; i<=m; i++)
{
//cin>>a>>b>>w;
scanf("%d%d%lf",&a,&b,&w);
add(a,b,w);
l=min(l,w);
r=max(r,w);
}
//cout<<m<<endl;
printf("Case #%d: ",case2++);
//if(m==1)
if(!check(r+1))//还有这里就是如果我减掉最大值+1,还是不能构成负环,那么就说明无法成环
printf("No cycle found.\n");
else
{
double s=1e-8;
while(r-l>s)
{
mid=(r+l)/2.0;
if(check(mid))
{
r=mid;
}
else l=mid;
}
printf("%.2lf\n",r);
}
}
return 0;
}