单源最短路径与每对结点最短路径—迪杰斯特拉与弗洛伊德

    在一个图中,若从一个结点到另一个结点存在着路径,定义路径长度为一条路径上所经过的边的权值之和。从一个结点到令一个结点可能存在着多条路径,路径最短的那条称作最短路径。

    Dijkastra思想(不能包含负权值的边)

    设置两个结点集合S和T,S中存放已找到最短路径的结点,T中存放当前还未找到最短路径的结点。初始状态S中只包含起始点u0。然后不断的从集合T中选择到u0路径长度最短的结点v,每次加入,都要修改从源点u0到集合T中剩余结点的当前最短路径长度,为原来路径长度与u0经过v到达该结点的路径长度中的较小者。当T为空,算法结束。

    Dijkastra设计:

    distance数组用来记录从源点到其余个点的当前最短距离数值。

 

#include <iostream>
using namespace std;
const int MAX = 200000000;
int n;						//物品总数
int m;						//等级差距*
int graph[101][101];		//图
int d[101];					//从开始点到其余个点的花费
int value[101];				//物品本身的价值*
int level[101];				//物品等级*
bool s[101],in_limit[101];	//标记数组*

int dijkastra()
{
	int result = MAX;
	int i,j;
	int k,dis;
	memset(s,0,sizeof(s));
	for(i = 2,d[1] = 0; i <= n; i++)
		d[i] = MAX;
	for(i = 1; i <= n; i++)
	{
		k = 0;
		dis = MAX;
		for(j = 1; j <= n; j++)
			if(!s[j] && d[j] <= dis && in_limit[j])
			{
				k = j;
				dis = d[j];
			}
		s[k] = 1;
		for(j = 1; j <= n; j++)
		{
			if(in_limit[j] && d[j] > d[k] + graph[k][j])
				d[j] = d[k] + graph[k][j];
		}
	}
	for(i = 1; i <= n; i++)
	{
		d[i] += value[i];
		if(d[i] < result)
			result = d[i];
	}
	return result;
}

int main()
{
	int i,j;
	int result = MAX,t = 0;
	int t1,v1;
	int x;
	cin>>m>>n;
	for(i=0;i<=n;i++)
		for(j=0;j<=n;j++)
			if(i == j)
				graph[i][j] = 0;
			else
				graph[i][j] = MAX;
	for(i = 1; i <= n; i++)
	{
		cin>>value[i]>>level[i]>>x;
		for(j = 1; j <= x; j++)
		{
			cin>>t1>>v1;
			graph[i][t1] = v1;
		}
	}
	for(i = 0; i <= m; i++)
	{
		memset(in_limit,0,sizeof(in_limit));
		for(j = 1; j <= n; j++)
			if(level[j] >= level[1]-m+i && level[j] <= level[1]+i)
				in_limit[j] = 1;
		t = dijkastra();
		if(result > t)
			result = t;
	}
	cout<<result<<endl;
	return 0;
}

 

    上述迪杰斯特拉算法可以求出某个结点与其他结点的最短路径,现在考虑这样一个问题:求出图中每对顶点的最短路径。用简单的方法,把每个顶点作为源点,用迪杰斯特拉,可以求出每对结点的最短路径。其实还有更简单的方法——弗洛伊德算法。(代码简单,思维复杂)

    folyd思想(动归,可以包含负权值的边,但不能有负环

    将图G中顶点编号,从1到n。现在考虑从i到j的最短路径,若k是这条路上的中间结点,那么由i到k和由k到j的这两条子路径应分别是由i到k和由k到j的最短路径。否则,这条由i到j的路径就不是具有最小长度的路径。于是,最优性原理成立,这表明有使用动态规划的可能性。如果k是编号最高的中间结点,那么由i到k的这条路径上就不会有比k-1更大的结点通过。同样,由k到j路径上也不会有比k-1更大的结点通过。因此,可以把求取一条由i到j的最短路径看成如下过程:首先需要决策哪一个结点是该路径上具有最大编号的中间结点k,然后就再去求由i到k和由k到j这两对结点间的最短路径。当然,这两条路径都不可能有比k-1还大的中间结点。

则得到递推式:

A(i,j)k = min{A(i,j)k-1 , A(i,k)k-1 + A(k,j)k-1)}, k>=1

 

import java.io.BufferedInputStream;
import java.util.Scanner;

public class Main {
	public static int MAX = 991;
	public static int[][] map = new int[101][101];
	public static int n;
	
	public static void floyd() {
		int i, j, k;
		for (k = 1; k <= n; k++) {
			for (i = 1; i <= n; i++) {
				for (j = 1; j <= n; j++) {
					if (i != k && j != k) {
						if (map[i][k] + map[k][j] < map[i][j])
							map[i][j] = map[i][k] + map[k][j];
					}
				}
			}
		}
	}
	
	public static void initial() {
		int i, j;
		for (i = 1; i <= n; i++)
			for (j = 1; j <= n; j++)
				map[i][j] = MAX;
	}
	
	public static void main(String[] args) {
		int i,j,t,a;
		int result1,result2;
		boolean flag = false;
		Scanner cin = new Scanner(new BufferedInputStream(System.in));
		while((n = cin.nextInt()) != 0) {
			result1 = 0;
			result2 = MAX;
			initial();
			for(i = 1;i <= n; i++) {
				t = cin.nextInt();
				for (j = 1; j <= t; j++) {
					a = cin.nextInt();
					map[i][a] = cin.nextInt();
				}
			}
			floyd();
			flag = false;
			for (i = 1; i <= n; i++) {
				t = 0;
				for (j = 1; j <= n; j++) {
					if (i != j) {
						if (map[i][j] == MAX) {
							t = 0;
							break;
						}
						if (map[i][j] > t)
							t = map[i][j];
					}
				}
				if (t != 0) {
					flag = true;
					if (t < result2) {
						result1 = i;
						result2 = t;
					}
				}
			}
			if(!flag)
				System.out.println("disjoint");
			else
				System.out.println(result1 + " " +result2);
		}
	}
}

 

 参见poj1062,1125

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值