UVA 11090 Going in Cycle!! 环平均权值(bellman-ford,spfa,二分)

时间:2023-12-28 10:03:50

题意:

  给定一个n个点m条边的带权有向图,求平均权值最小的回路的平均权值?

思路:

  首先,图中得有环的存在才有解,其次再解决这个最小平均权值为多少。一般这种就是二分猜平均权值了,因为环在哪也难以找出来,还有可能是一条边属于多个环。对于每个猜到的平均值,如果对应环的存在,那么这个环的每条边的权减去这个平均值之后,用spfa算法就能判断其是否有环的存在即可。

  假设环上各边权值为:w1+w2+...+wk。

  式子:w1+w2+...+wk<k*even   相当于   (w1-even)+(w2-even)+...(wk-even)< 0。即更新完边权后应i该是有环存在的。

  对于猜测的平均权值mid,如果不能找到环,则说明mid应该更大。

 #include <bits/stdc++.h>
#define LL long long
#define pii pair<int,int>
#define INF 0x7f7f7f7f
using namespace std;
const int N=;
vector<int> vect[N];
struct node
{
int from,to;
double cost;
node(){};
node(int from,int to,int cost):from(from),to(to),cost(cost){};
}edge[N];
int edge_cnt;
int big, small;
void add_node(int from,int to,double cost)
{
edge[edge_cnt]=node(from, to, cost);
vect[from].push_back(edge_cnt++);
} int inq[N], cnt[N];
double dist[N];
bool spfa(int n, double q)
{
memset(inq,,sizeof(inq));
memset(cnt,,sizeof(cnt));
deque<int> que;
for(int i=; i<=n; i++) dist[i]=0.0, inq[i]=, que.push_back(i); //因为是判断负环的,所以dist初始化为0即可。
while(!que.empty())
{
int x=que.front();que.pop_front();
inq[x]=;
for(int i=; i<vect[x].size(); i++)
{
node e=edge[vect[x][i]];
if(dist[e.to]>dist[x]+e.cost-q )
{
dist[e.to]=dist[x]+e.cost-q ;
if(!inq[e.to])
{
inq[e.to]=;
que.push_back(e.to);
if(++cnt[e.to]>n)
return true;
}
}
}
}
return false;
} double cal(int n)
{
double l=small, r=big, ans=0.0;
while(r-l>1e-)
{
double mid=(l+r)/;
if( spfa(n, mid) ) r=mid; //有负环
else l=mid;
}
return l;
} int main()
{
// freopen("input.txt", "r", stdin);
int n, m, t, a, b, c, j=;
cin>>t;
while(t--)
{
scanf("%d%d",&n,&m);
edge_cnt=;
for(int i=; i<=n; i++) vect[i].clear();
memset(edge,,sizeof(edge));
big=;
small=INF; for(int i=; i<m; i++)
{
scanf("%d%d%d",&a,&b,&c);
add_node(a,b,c);
small=min(small,c);
big=max(big, c);
}
if( !spfa(n, big+) ) printf("Case #%d: No cycle found.\n", ++j);
else printf("Case #%d: %.2f\n", ++j, cal(n));
}
return ;
}

AC代码