给你一个边权为正的有向图,节点数最多50个。问你平均长度最短的圈的平均长度是多少。如果没有就输出没有,有就输出具体数值。
显然圈有很多,每个圈的长度也有很多。最简单的思路就是找到所有长度,或者所有最短长度,然后从中挑一个最小的就好了,但是好像没有什么算法能找到所有圈以及他们的所有长度或最短长度的。我最多知道如何判断一个图中有没有圈,而且这样的算法还不少。比如dfs,Floyd判圈法,Bellman-Ford等。。。
题目只是问你最短的长度是多少。所以也没有必要找出所有的圈。由于圈的长度是全局的性质,所以也很难通过dfs来计算长度。似乎和长度有关的圈的算法我就只会Bellman-Ford了。“判断有没有”的算法和二分往往是好搭档,这个组合经常出现在找最优解的问题上,当然前面也有不少和其他算法组合的题目,但是二分最为经典。本题也不例外。Bellman-Ford是关于负圈的,所以应该想到要在权值上做文章。题目求的是平均长度,所以不但和总长度有关,还和点的个数有关。而Bellman-Ford算法就是一个能总览全局的算法。不像dfs那样只能在局部思考。
具体就是二分长度X,然后建图,边的权值为W-X,如果存在负环说明存在平均长度为X的圈,否则不存在。
自己没有做出来,希望通过这道题目自己能学到一些思想吧。
大概就是要学会使用已有的工具去解决一些特别的问题吧。
又RE了。。。
边数组的大小一定要是maxn*maxn,别开小了(无重边,自环)
代码
#include<bits/stdc++.h>
#include<limits.h>
using namespace std;
const int maxn = 55;
const int maxm = maxn*maxn;
const double eps =1e-3;
struct Edge
{
int from,to;
double dist;
};
struct Bellman_ford
{
int n,m;
vector<Edge>edges;
vector<int>G[maxn];
double d[maxn];
int inq[maxn],cnt[maxn];
void init(int n)
{
this->n=n;
for(int i=0;i<n;i++) G[i].clear();
edges.clear();
}
void add(int u,int v,double dist)
{
edges.push_back((Edge){u,v,dist});
m=edges.size();
G[u].push_back(m-1);
}
bool circle()
{
queue<int>Q;
for(int i=0;i<n;i++)
{
d[i]=cnt[i]=0;
inq[i]=1;
Q.push(i);
}
while(!Q.empty())
{
int u=Q.front();Q.pop();
inq[u]=0;
for(unsigned int i=0;i<G[u].size();i++)
{
Edge& e=edges[G[u][i]];
if(d[e.to]>d[e.from]+e.dist)
{
d[e.to]=d[e.from]+e.dist;
if(!inq[e.to])
{
inq[e.to]=1;
Q.push(e.to);
if(++cnt[e.to]>n) return true;
}
}
}
}
return false;
}
};
Bellman_ford BF;
int u[maxm],v[maxm];
double w[maxm];
int main()
{
int T,n,m;
scanf("%d",&T);
for(int t=1;t<=T;t++)
{
scanf("%d %d",&n,&m);
double MAX=0;
for(int i=0;i<m;i++)
{
scanf("%d %d %lf",u+i,v+i,w+i);
u[i]--;
v[i]--;
MAX+=w[i];
}
MAX=MAX*2;
double l=0,r=MAX;
while(r-l>eps)
{
double mid=l+(r-l)/2;
BF.init(n);
for(int i=0;i<m;i++)
BF.add(u[i],v[i],w[i]-mid);
if(BF.circle()) r=mid;
else l=mid;
}
printf("Case #%d: ",t);
if(l<eps||MAX-r<eps) puts("No cycle found.");
else printf("%.2lf\n",l);
}
return 0;
}