浙大2020年Mooc数据结构笔记–第七讲 图(中)
〇、前言
- 这几天开始跟着学数据结构,鉴于当初数据结构实在学的太弱,加之这项工作算是为大家之后复习、机试铺路。确实是一个迫切需要做的大规模工作。行胜于言,虽然从第二篇开始,坚持下去。
- 此文部分为自己写,部分参考网上内容。提前说一下哈。期待批评指正。
一、Dijkstra算法
Dijkstra主要解决单源最短路径问题
二、Floyd算法
解决多源最短路径问题
三、哈利波特的考试
三、课后题
1、07-图4 哈利·波特的考试 (25分)
输入样例:
6 11
3 4 70
1 2 1
5 4 50
2 6 50
5 6 60
1 3 70
4 6 60
3 6 80
5 1 100
2 4 60
5 2 80
输出样例:
4 70
#include<iostream>
#include<string.h>
using namespace std;
#define MAX 100
#define INF 65535
int N = 0; //节点个数
int M = 0; //边的个数
int G[MAX][MAX] = { 0 };
int D[MAX][MAX] = { 0 };
void initD() {
for (int i = 0; i < MAX; i++) {
for (int j = 0; j < MAX; j++) {
D[i][j] = INF;
}
}
}
void initG() {
for (int i = 0; i < MAX; i++) {
for (int j = 0; j < MAX; j++) {
G[i][j] = INF;
}
}
}
void Floyd() {
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
D[i][j] = G[i][j];
}
}
for (int i = 0; i < N; i++) {
D[i][i] = 0;
}
printf("1\n");
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
printf("%d ", D[i][j]);
}
printf("\n");
}
for (int k = 0; k < N; k++) {
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (D[i][k] !=INF&& D[k][j] !=INF&&D[i][k] + D[k][j] < D[i][j]) {
D[i][j] = D[i][k] + D[k][j];
}
}
}
}
}
//找每行最大值的最小值
void FindAnimal() {
int dis = INF;//记录最后的距离
int k = 0;//记录最后拿的动物
for (int i = 0; i < N; i++) {
int max = 0;
for (int j = 0; j < N; j++) {
if (max < D[i][j]) {
max = D[i][j];
}
}
if (max < dis) {
dis = max;
k = i+1;
}
}
printf("%d %d", k, dis);
}
int main() {
int eage1, eage2;
int weight;
initG();
scanf("%d %d", &N, &M); //N 为结点数,M为边数
for (int i = 0; i < M; i++) {
scanf("%d %d %d", &eage1, &eage2,&weight);
G[eage1 - 1][eage2 - 1] = weight;
G[eage2 - 1][eage1 - 1] = weight;
}
initD(); // 初始化D[][]数组
Floyd();
printf("2\n");
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
printf("%d ", D[i][j]);
}
printf("\n");
}
FindAnimal();
return 0;
}
2、07-图5 Saving James Bond - Hard Version (30分)
Sample Input 1:
17 15
10 -21
10 21
-40 10
30 -50
20 40
35 10
0 -10
-25 22
40 -40
-30 30
-10 22
0 11
25 21
25 10
10 10
10 35
-30 10
Sample Output 1:
4
0 11
10 21
10 35
Sample Input 2:
4 13
-12 12
12 12
-12 -12
12 -12
Sample Output 2:
0
将起点集合和终点集合找到,使用Floyd 多源最短路径找出最短距离即可,并用path二维矩阵记录路径。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;
const int MAXV = 110;
const int INF = 65535;
using namespace std;
struct point{
int x,y;
}p[MAXV];
const double RADIUS = 7.5; //半径
int N,D;
int G[MAXV][MAXV],path[MAXV][MAXV];
vector<int> first,last;
double getDis(int i, int j){ //计算两点间距离
return sqrt(pow(p[i].x - p[j].x, 2) + pow(p[i].y - p[j].y,2));
}
bool cmp(int i,int j){ //排序函数中的比较函数。把第一次能跳上的鳄鱼按照近小岛的距离升序排列
return getDis(i,0) < getDis(j,0);
}
bool isFirst(int i){ //第一跳能到达
return getDis(i,0) <= D + RADIUS;
}
bool isLast(int i){ //最后一跳能到达
return (abs(p[i].x) + D >= 50||abs(p[i].y) + D >= 50);
}
void getWay(int st, int ed){ //打印出经过的鳄鱼的坐标
printf("%d %d\n",p[st].x, p[st].y);
while(st != ed){
st = path[st][ed];
printf("%d %d\n",p[st].x,p[st].y);
}
}
void Floyd(){
for(int k = 1; k <= N; k++){
for(int i=1; i <= N; i++){
for(int j = 1; j <= N; j++){
if(G[i][k] + G[k][j] < G[i][j]){
G[i][j] = G[i][k] + G[k][j];
path[i][j] = path[i][k];
}
}
}
}
}
int main()
{
fill(G[0], G[0] + MAXV*MAXV, INF); //初始化图
for(int i = 1; i < MAXV; i++)
for(int j = 1; j < MAXV; j++)
path[i][j] = j; //初始化路径
cin>>N>>D;
if(D >= 50-RADIUS){ //一步到岸边
printf("1\n");
return 0;
}
p[0].x = p[0].y = 0;
for(int i = 1; i <= N; i++){ //数组下标1开始存储数据
cin>>p[i].x>>p[i].y;
if((getDis(i,0) <= RADIUS)||abs(p[i].x) >= 50||abs(p[i].y) >= 50) { //当鳄鱼在岸上或在小岛上时,这条鳄鱼可以忽略不计。因此鳄鱼数量N减一,下条鳄鱼顶替它的数组中的位置
i--;
N--;
}
}
for(int i = 1; i <= N; i++){
for(int j = 1; j <= N; j++){
if(getDis(i,j) <= D){
if(i == j)
G[i][j] = G[j][i] = 0;
else
G[i][j] = G[j][i] = 1;
}
}
}
for(int i=1;i<=N;i++)//加入起点集
if(isFirst(i))
first.push_back(i);
for(int i=1;i<=N;i++){ //加入终点集
if(isLast(i))
last.push_back(i);
}
Floyd();
sort(first.begin(), first.end(), cmp); //起点集按与小岛的距离从小到达排序
int minStep = INF, st, ed; //分别为最小的跳跃数,起点下标,终点下标
for(int i=0; i < first.size(); i++){
int u = first[i];
for(int j = 0; j < last.size(); j++){
int v = last[j];
if(G[u][v] < minStep){
minStep = G[u][v];
st = u;
ed = v;
}
}
}
if(minStep != INF){
printf("%d\n",minStep + 2); //minStep为起点到终点的距离,加上从小岛跳到起点鳄鱼,从终点鳄鱼跳到岸边的2步
getWay(st,ed);//打印路径
}else //当最小的跳跃数为INF,则不可能逃脱
printf("0");
return 0;
}
3、07-图6 旅游规划 (25分)
输入样例:
4 5 0 3
0 1 1 20
1 3 2 30
0 3 4 10
0 2 2 20
2 3 1 20
输出样例:
3 40
#include <stdio.h>
#define INFINITY 100000000
#define ERROR -1
typedef int Vertex;
struct Gnode {
int weight;
int expenses;
};
struct Gnode G[500][500];
int Nv, Ne;//边数控制输入行数,顶点数控制for循环范围
int strpos, despos;//起点,终点
int visited[500] = { 0 };
int dist[500], cost[500];//最短路径以及最小花费
void buildgraph() {
int v1, v2, w, ex;
int i, j;
//初始化图
for (i = 0; i < Nv; i++) {
for (j = 0; j < Nv; j++) {
G[i][j].weight = INFINITY;
G[i][j].expenses = INFINITY;
}
G[i][i].weight = 0;
G[i][i].expenses = 0;
dist[i] = INFINITY;
cost[i] = INFINITY;
}
for (i = 0; i < Ne; i++) {
scanf("%d %d %d %d", &v1, &v2, &w, &ex);
G[v1][v2].weight = w;
G[v1][v2].expenses = ex;
G[v2][v1].weight = w;
G[v2][v1].expenses = ex;
}
}
/* 邻接矩阵存储 - 有权图的单源最短路算法 */
Vertex FindMinDist() {
/* 返回未被收录顶点中dist最小者 */
int MinV, V;
int MinDist = INFINITY;
for (V = 0; V < Nv; V++) {
if (!visited[V] && dist[V] < MinDist) {
/* 若V未被收录,且dist[V]更小 */
MinDist = dist[V]; /* 更新最小距离 */
MinV = V; /* 更新对应顶点 */
}
}
if (MinDist < INFINITY) /* 若找到最小dist */
return MinV; /* 返回对应的顶点下标 */
else
return ERROR; /* 若这样的顶点不存在,返回错误标记 */
}
int Dijkstra() {
Vertex V, W;
/* 初始化:此处默认邻接矩阵中不存在的边用INFINITY表示 */
for (V = 0; V < Nv; V++) {
dist[V] = G[strpos][V].weight;
cost[V] = G[strpos][V].expenses;
}
/* 先将起点收入集合 */
dist[strpos] = cost[strpos] = 0;
visited[strpos] = 1;//对起点进行初始化
while (1) {
/* V = 未被收录顶点中dist最小者 */
V = FindMinDist();
if (V == ERROR) /* 若这样的V不存在 */
break; /* 算法结束 */
visited[V] = 1; /* 收录V */
for (W = 0; W < Nv; W++) /* 对图中的每个顶点W */
/* 若W是V的邻接点并且未被收录 */
if (!visited[W] && G[V][W].weight < INFINITY) {
if (dist[V] + G[V][W].weight < dist[W]) {
dist[W] = dist[V] + G[V][W].weight;
cost[W] = cost[V] + G[V][W].expenses;
}
else if ((dist[V] + G[V][W].weight == dist[W]) && (cost[V] + G[V][W].expenses < cost[W]))
cost[W] = cost[V] + G[V][W].expenses;
}
} /* while结束*/
return 1; /* 算法执行完毕,返回正确标记 */
}
int main() {
scanf("%d %d %d %d", &Nv, &Ne, &strpos, &despos);
buildgraph();
Dijkstra();
printf("%d %d", dist[despos], cost[despos]);
return 0;
}