本来以为是Floyd求最小环的模板题,然而,然而,似乎不是。但其实这道题求的是边权平均值最小的环,所以貌似不能用Floyd,因为它不一定能遍历
图中所有的环,多么痛的领悟,一个晚上,调不出来。
也或许是我姿势不对。(代码在最后面,有大神能指出我错在哪里,必万分感谢)
正确做法:二分+SPFA
二分答案,将所有边权减去二分的那个值,然后spfa判断是否有负环,
如果存在负环说明当前值太大了,r=mid;
如果不存在负环说明当前值太小了,l=mid;
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <stdlib.h>
#include <stack>
#include <vector>
#include <string.h>
#include <queue>
#define msc(X) memset(X,-1,sizeof(X))
#define ms(X) memset(X,0,sizeof(X))
typedef long long LL;
using namespace std;
const int MAXN=59;
const int INF=0x3f3f3f3f;
const double eps=1e-6;
int dt[MAXN][MAXN],cnt[MAXN],n,m;
double d[MAXN];
bool vs[MAXN];
bool SPFA(int beg,double cut)
{
ms(vs);
for(int i=1;i<=n;i++) d[i]=1e14;
vs[beg]=true;
d[beg]=0;
queue<int> que;
while(!que.empty()) que.pop();
que.push(beg);
ms(cnt);
cnt[beg]=1;
while(!que.empty()){
int u=que.front();
que.pop();
vs[u]=false;
for(int j=1;j<=n;j++)
{
if(dt[u][j]==INF) continue;
if(d[j]>d[u]+dt[u][j]-cut){
d[j]=d[u]+dt[u][j]-cut;
if(!vs[j]) {
if(++cnt[j]>n) return false;
vs[j]=true;
que.push(j);
}
}
}
}
return true;
}
int main(int argc, char const *argv[])
{
int N,ti=0;
cin>>N;
while(++ti<=N){
scanf("%d %d",&n,&m);
int maxw=0;
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j++)
dt[j][i]=dt[i][j]=INF;//d[i][i]应该是INF,而不是0
while(m--){
int u,v,w;
scanf("%d %d %d",&u,&v,&w);
maxw=max(maxw,w);
dt[u][v]=min(w,dt[u][v]);
}
double l=0.0,r=maxw+2.0;
while(r-l>eps){
double mid=(l+r)*0.5;
bool flg=false;
for(int i=1;i<=n;i++)
if(!SPFA(i,mid)) {flg=true;break;}
if(flg) r=mid;
else l=mid;
}
printf("Case #%d: ",ti );
if(r>maxw+1.0) puts("No cycle found.");//不要写r>maxw,精度啊=。=|||
else printf("%.2f\n",r );
}
return 0;
}
wa到死的Floyd
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <stdlib.h>
#include <stack>
#include <vector>
#include <string.h>
#include <queue>
#define msc(X) memset(X,-1,sizeof(X))
#define ms(X) memset(X,0,sizeof(X))
typedef long long LL;
using namespace std;
const int MAXN=59;
const int INF=0x3f3f3f3f;
int mp[MAXN][MAXN],dt[MAXN][MAXN];
int fa[MAXN][MAXN];
double res;
void Floyd(int n)
{
for(int k=1;k<=n;k++)
{
for(int i=1;i<k;i++)//一定是<k
for(int j=1;j<k;j++)
{
if(mp[i][k]==INF||mp[k][j]==INF||dt[j][i]==INF) continue;
int tmp=mp[i][k]+mp[k][j]+dt[j][i];//注意不要写成dt[i][j]
int num=0,p=j;
while(p!=i)//逆向寻找前驱结点直到找到最前面的i,i->…->j
{
num++;
p=fa[i][p];//fa[i][j]保存的不是k,而是fa[k][j].
}
num+=2;
double ts=(double)tmp/(double)num;
res=min(res,ts);
}
//正常的Floyd
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
int tmp=dt[i][k]+dt[k][j];
if(tmp<dt[i][j]){
dt[i][j]=tmp;
fa[i][j]=fa[k][j];
}
}
}
}
int main(int argc, char const *argv[])
{
int N,ti=0;
cin>>N;
while(++ti<=N){
int n,m;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if(i==j) mp[i][j]=dt[i][j]=0;//如果可以两个点,值=0,否则INF
else mp[i][j]=dt[i][j]=INF;
fa[i][j]=i;
}
while(m--){
int u,v,w;
scanf("%d %d %d",&u,&v,&w);
if(mp[u][v]<w) w=mp[u][v];
mp[u][v]=dt[u][v]=w;
}
res=1e14;
Floyd(n);
printf("Case #%d: ",ti );
if(res==1e14) puts("No cycle found.");
else printf("%.2f\n",res );
}
return 0;
}