题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4650
题目大意:
给定一个无重边的有向图,
求全源最小平均权值路径。
最小平均权值路径的定义是,路径总权值 / 路径上的边数最小。
算法:
题目应当分为两种情况:
两点间的路径上可能存在负环,如果一条路径沿着这个负环不断走下去,那么最终的平均值一定是趋于这个负环的平均权值。
无负环,那么这个显然很好求。
做这道题首先要知道一种用矩阵求两点间恰好K条边的最短路径的方法。不清楚的话建议去做POJ 3613。
首先,利用矩阵乘法,由1~n枚举路径长度,然后根据此时各点间的最短路 / 路径长度,不断更新此时的最小平均权值路径。
然后,讨论图上的各个环,如果某点有负环,那么对于任两个可以到达这个点的点对,这个负环的平均权值可能会更新这个点对上的最小平均权值。
PS:这算是我做过的少有的,在赛场上写出来且代码量极小,却可以把它归为中高级图论的题吧。笑。
代码如下:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<sstream>
#include<cstdlib>
#include<cstring>
#include<string>
#include<climits>
#include<cmath>
#include<queue>
#include<vector>
#include<stack>
#include<set>
#include<map>
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
const int MAXN=110;
int vis[MAXN][MAXN];
double ans[MAXN][MAXN];
int tot;
struct Matrix
{
int h,w;
int mx[MAXN][MAXN];
Matrix operator* (const Matrix& b) const
{
Matrix tmp;
memset(tmp.mx,INF,sizeof(tmp.mx));
tmp.h=h;
tmp.w=b.w;
for (int i=1; i<=h; i++)
{
for (int j=1; j<=b.w; j++)
{
for (int k=1; k<=w; k++)
{
if(vis[i][k]==-1||tot<vis[i][k])
{
continue;
}
if(vis[k][j]==-1||tot<vis[k][j])
{
continue;
}
tmp.mx[i][j]=min(tmp.mx[i][j],mx[i][k]+b.mx[k][j]);
}
}
}
return tmp;
}
};
Matrix g,f;
int main()
{
int n, m;
while(scanf("%d %d",&n, &m)==2)
{
memset(g.mx,INF,sizeof(g.mx));
g.h=g.w=n;
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
ans[i][j]=1e7;
}
}
memset(vis,-1,sizeof(vis));
for(int i=0; i<m; i++)
{
int u,v;
scanf("%d%d",&u,&v);
scanf("%d",&g.mx[u][v]);
vis[u][v]=1;
}
for(int k=1; k<=n; k++)
{
for(int i=1; i<=n; i++)
{
if(vis[i][k]==-1)
{
continue;
}
for(int j=1; j<=n; j++)
{
if(vis[k][j]==-1)
{
continue;
}
vis[i][j]=(vis[i][j]==-1)?vis[i][k]+vis[k][j]:min(vis[i][j],vis[i][k]+vis[k][j]);
}
}
}
f=g;
for(tot=1; tot<=n; tot++)
{
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
if(vis[i][j]!=-1&&tot>=vis[i][j])
{
ans[i][j]=min(ans[i][j],(double)f.mx[i][j]/tot);
}
}
}
f=f*g;
}
for(int k=1; k<=n; k++)
{
for(int i=1; i<=n; i++)
{
if(vis[i][k]==-1)
{
continue;
}
for(int j=1; j<=n; j++)
{
if(vis[k][j]==-1||vis[k][k]==-1)
{
continue;
}
ans[i][j]=min(ans[i][j],ans[k][k]);
}
}
}
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
if(j>1)
{
putchar(' ');
}
if(vis[i][j]!=-1)
{
printf("%.3lf",ans[i][j]);
}
else
{
printf("NO");
}
}
puts("");
}
}
return 0;
}