hdu 4650 Minimum Average Weight Path

Minimum Average Weight Path

Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 114    Accepted Submission(s): 54


Problem Description
In mathematics, a graph is a representation of a set of objects where some pairs of the objects are connected by links. The interconnected objects are represented by mathematical abstractions called vertices, and the links that connect some pairs of vertices are called edges. A path in a graph is a sequence of vertices, and for any 2 adjacent u, v, there is a edge from u to v in graph. A path contains at least one edge. In the graph in Sample 2, {3, 3, 2, 2} can form a path from 3 to 2. 

One of the common problem is to find the shortest path between two certain vertices, or all of them. They've been well studied as the single source shortest path problem (SSSP) and the all pairs shortest paths problem (APSP).

In this problem, we'll provide you a derivation analogous to APSP. You've been given a directed graph with positive or negative edge weights. We define the average weight of a path, as the sum of the edge weights divide the edges number of path. Now you need to find the minimum average weight between all pairs of vertices (APMAWP). 

 

Input
Muiltcases. The first line contains two integer n, m, (1 ≤ n ≤ 10 2, 1 ≤ m ≤ 10 4 ) the number of the vertices and the number of the edges.

The next m lines, each line contains three intergers u, v, w, representing a directed edge from u to v with weight w. (|w| ≤ 10 3)

There is no multi-edge. It can contain self-loops. 

 

Output
A n × n matrix representing the APMAWP. The j's element of the i's row represents the minimum average weight of all the paths from vertex i to vertex j. If no such path exists, you need to output "NO" instead (DO NOT output quote please). For each real number, you need to keep exactly 3 digits after digit point.

 

Sample Input
  
  
4 4 2 1 2 1 3 -8 2 4 -6 4 3 1 5 8 3 3 735 2 1 946 4 2 276 2 2 -990 3 2 -162 4 4 -18 3 5 783 5 5 -156
 

Sample Output
  
  
NO NO -8.000 NO 2.000 NO -3.000 -6.000 NO NO NO NO NO NO 1.000 NO NO NO NO NO NO -990.000 -990.000 NO NO NO -990.000 -990.000 735.000 NO -156.000 -990.000 -990.000 NO -18.000 NO NO NO NO NO -156.000
 

Source
 

Recommend
zhuyuanchen520

解题思路:

基本就是抄这个的: http://blog.csdn.net/nealgavin/article/details/9819371。比赛的时候没做出来
我阐述下这位的想法,
首先计算出任意两个点之间最少需要经过多少条边可达,记为vis[u][v]表示u到v最少需要经过vis[u][v]条边,如果不可达就是-1;
这个过程可以看成是floyd,n^3可求
然后分别计算经过k(1<=k<=n)条边的时候,u到v的最短路长度,并用它来更新答案ans[u][v]。
从1~n来枚举k,利用类似矩阵相乘的n^3方法可以计算经过k条边的最短路,更新ans是n^2。总的来说是n^4
最后是把环考虑进去,前面都是经过了k(k<=n)条边的时候的答案,对于有环的情况ans[x][x]其实是正确的答案,因为如果x->x有环的话,这个环最多包含n个点,这个环即使重复走多次也不会使ans[x][x]变小的,所以ans[x][x]此时是正确的。
所以就枚举i,j,k,如果有i->k->j的路径的话,就用ans[k][k]来尝试更新ans[i][j]就行了。

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int N = 101;
const double INF = 10000000000.0;
const int INF_INT =10000000;
int side[N][N];
int dis[N][N];
int vis[N][N];
int tmp[N][N];
double ans[N][N];
int main(){
	int n,m;
	//freopen("1008.in","r",stdin);
	//freopen("1008_my.out","w",stdout);
	while(~scanf("%d%d",&n,&m)){
		memset(vis,-1,sizeof(vis));
		for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){ans[i][j]=INF;side[i][j]=INF_INT;}
		for(int i=0;i<m;i++){
			int u,v,w;
			scanf("%d%d%d",&u,&v,&w);
			side[u][v]=w;
			vis[u][v]=1;
		}
		for(int k=1;k<=n;k++)for(int i=1;i<=n;i++)if(vis[i][k]!=-1){
			for(int j=1;j<=n;j++)if(vis[k][j]!=-1){
				if(vis[i][j]==-1)vis[i][j]=vis[i][k]+vis[k][j];
				else vis[i][j]=min(vis[i][j],vis[i][k]+vis[k][j]);
			}
		}
		for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)dis[i][j]=side[i][j];
		for(int k=1;k<=n;k++){
			for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(vis[i][j]!=-1&&k>=vis[i][j]){
				ans[i][j]=min(ans[i][j],(double)dis[i][j]/k);
			}
			for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){tmp[i][j]=dis[i][j];dis[i][j]=INF_INT;}
			for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){
				for(int h=1;h<=n;h++){
					if(vis[i][h]==-1||k<vis[i][h])continue;
					if(vis[h][j]==-1||k<vis[h][j])continue;
					dis[i][j]=min(dis[i][j],tmp[i][h]+side[h][j]);
				}
			}
		}
		for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(vis[i][j]!=-1){
			for(int k=1;k<=n;k++)if(vis[i][k]!=-1&&vis[k][j]!=-1&&vis[k][k]!=-1){
				ans[i][j]=min(ans[i][j],ans[k][k]);
			}
		}
		for(int i=1;i<=n;i++){
			int j=1;
			if(vis[i][j]==-1)printf("NO");
			else printf("%.3lf",ans[i][j]);
			for(j=2;j<=n;j++){
				if(vis[i][j]==-1)printf(" NO");
				else printf(" %.3lf",ans[i][j]);
			}
			printf("\n");
		}
	}
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值