时间限制 : 1000 ms
空间限制 : 32 MB
题目描述
给你n个点,m条无向边,每条边都有长度d和花费p,给你起点s终点t,要求输出起点到终点的最短距离及其花费,如果最短距
离有多条路线,则输出花费最少的。
输入描述:
输入n,m,点的编号是1~n,然后是m行,每行4个数 a,b,d,p,表示a和b之间有一条边,且其长度为d,花费为p。最后一行是两个数 s,t;起点s,终点t。n和m为0时输入结束。 (1<n<=1000, 0<m<100000, s != t)
输出描述:
输出 一行有两个数, 最短距离及其花费。
输入
3 2
1 2 5 6
2 3 4 5
1 3
0 0
输出
9 11
个人觉得这道题还挺复杂的,要考虑两个权重——距离、花费;
点数、边数的上限值n、m也很大;
采用Dijkstra(迪杰斯特拉)算法。
#include <iostream>
#include <limits>
#define Vmax 1001
using namespace std;
int inf = numeric_limits<int>::max(); //相当于无穷
int lenth[Vmax][Vmax]; //边的长度邻接矩阵
int value[Vmax][Vmax]; //边的权重邻接矩阵
void shortestPath(int start, int end, int Vnum) {
int dist[Vmax]; //s到i的最短路径长度记为dist[i]
int cost[Vmax]; //s到i的最短路径花费记为cost[i]
bool v[Vmax]; //记录已求得最短路径的顶点集
int i, j;
for (i = 1; i <= Vnum; i++) {
dist[i] = lenth[start][i]; //最短距离先设为直接距离
cost[i] = value[start][i];
v[i] = false; //均未求得最短路径
}
v[start] = true;
//
for (i = 1; i <= Vnum; i++) {
int minDist = inf;
int index = 0; //寻找与s距离最近的邻点
for (j = 1; j <= Vnum; j++) {
if (!v[j] && dist[j]<minDist) {
minDist = dist[j];
index = j;
}
}
v[index] = true; //将最近的邻点加入集合v中
for (j = 1; j <= Vnum; j++) {
if (!v[j] && dist[index] + lenth[index][j]<dist[j]) { //j不在v中且以index为中转的路径更短
dist[j] = dist[index] + lenth[index][j];
cost[j] = cost[index] + value[index][j];
}
else if (!v[j] && dist[index] + lenth[index][j] == dist[j] && cost[index] + value[index][j]<cost[j]) {
//路径一样但花费更少
cost[j] = cost[index] + value[index][j];
}
}
}
cout << dist[end] << " " << cost[end];
}
int main() {
int n, m, a, b, d, p, s, t;
int i, j;
while (cin >> n >> m, m || n) {
for (i = 1; i <= n; i++) { //初始化邻接矩阵
for (j = 1; j <= n; j++) {
if (i == j) lenth[i][j] = 0;
else lenth[i][j] = inf;
}
}
for (i = 0; i<m; i++) { //读入数据
cin >> a >> b >> d >> p;
lenth[a][b] = lenth[b][a] = d;
value[a][b] = value[b][a] = p;
}
cin >> s >> t;
shortestPath(s, t, n);
}
return 0;
}
Dijkstra(迪杰斯特拉)算法的核心思想是: 按路径长度递增的次序产生最短路径
假设图中有n个点,a为起点,用数组dist[n]来存放当前所找到a到每个终点的最短路径。
如,dist[i]表示当前a到i的最短路径长度。
初始时,dist[i]的值就为a、i之间的直接距离(如存在边,则为边的权重,否则为+∞);
在dist[n]中,找到最小值dist[k],这就是从a出发的长度最短的一条最短路径。
那么下一条长度次短的最短路径是哪一条呢?
假设V为以求得最短路径的终点的集合(程序中用bool数组v,v[i]表示某点i是否在V中),可以证明:
下一条最短路径(设其终点为x),只能是弧<s,x>,或者是中间只经过V中的顶点而到达x的路径。
反证法:假设下一条最短路径为(s,t,x),其中t不在V中,那么由于(s,t)必然小于(s,t,x),所以下一条最短的路径就不会是(s,t,x),因为至少(s,t)比它更短。
所以算法过程描述如下:
设s为起点,图的点集为E,集合V为已求得最短路径长度的终点
(1)对于不属于V的点,找到dist中的最小值dist[k],将k加入V中
(2)以k为中转点,更新s到还未加入V中的点的最短路径长度。
重复(1)、(2),直到所有的点加入集合V。
题目名称 : 最短路径2
时间限制 : 1000 ms
空间限制 : 32 MB
题目描述
N个城市,标号从0到N-1,M条道路,第K条道路(K从0开始)的长度为2^K,求编号为0的城市到其他城市的最短距离
输入描述:
第一行两个正整数N(2<=N<=100)M(M<=500),表示有N个城市,M条道路
接下来M行两个整数,表示相连的两个城市的编号
输出描述:
N-1行,表示0号城市到其他城市的最短路,如果无法到达,输出-1。
输入
4 4
1 2
2 3
1 3
0 1
输出
8
9
11
不得不吐槽一下,TA在matrix上给的测试样例真的很弱= =,已经查出三道题写得有问题但是在matrix上都通过了,包括这道题,想想如果M达到500的话,2^500将是一个非常巨大的数字,int肯定会溢出的,可是当时并没有处理,为了偷懒,直接Floyd吧,这道题应该也不会考吧(ಡωಡ)
#include<iostream>
#include <math.h>
using namespace std;
int path[105][105];
int main() {
int n, m,x,y;
while (cin >> n >> m) {
memset(path, -1, sizeof(path));
for (int i = 0; i < m; i++) {
cin >> x >> y;
path[x][y] = path[y][x] = pow(2, i);
}
for(int k=0;k<n;k++)
for(int i=0;i<n;i++)
for (int j = 0; j < n; j++) {
if (path[i][k] == -1 || path[k][j] == -1) continue;
if (path[i][k] + path[k][j] < path[i][j] || path[i][j]==-1)
path[i][j] = path[i][k] + path[k][j];
}
for (int i = 1; i < n; i++)
cout << path[0][i] << endl;
}
return 0;
}