算法课作业——图的遍历

返回主目录

6-1 Strongly Connected Components (25分)

题目描述

Write a program to find the strongly connected components in a digraph.

Format of functions:

void StronglyConnectedComponents( Graph G, void (*visit)(Vertex V) );

where Graph is defined as the following:

typedef struct VNode *PtrToVNode;
struct VNode {
    Vertex Vert;
    PtrToVNode Next;
};
typedef struct GNode *Graph;
struct GNode {
    int NumOfVertices;
    int NumOfEdges;
    PtrToVNode *Array;
};

Here void (*visit)(Vertex V)is a function parameter that is passed into StronglyConnectedComponentsto handle (print with a certain format) each vertex that is visited. The function StronglyConnectedComponentsis supposed to print a return after each component is found.

Sample program of judge:

#include <stdio.h>
#include <stdlib.h>

#define MaxVertices 10  /* maximum number of vertices */
typedef int Vertex;     /* vertices are numbered from 0 to MaxVertices-1 */
typedef struct VNode *PtrToVNode;
struct VNode {
    Vertex Vert;
    PtrToVNode Next;
};
typedef struct GNode *Graph;
struct GNode {
    int NumOfVertices;
    int NumOfEdges;
    PtrToVNode *Array;
};

Graph ReadG(); /* details omitted */

void PrintV( Vertex V )
{
   printf("%d ", V);
}

void StronglyConnectedComponents( Graph G, void (*visit)(Vertex V) );

int main()
{
    Graph G = ReadG();
    StronglyConnectedComponents( G, PrintV );
    return 0;
}

/* Your function will be put here */

Sample Input (for the graph shown in the figure):
在这里插入图片描述
4 5
0 1
1 2
2 0
3 1
3 2
Sample Output:
3
1 2 0
Note: The output order does not matter. That is, a solution like

0 1 2
3
is also considered correct.

代码实现

void StronglyConnectedComponents( Graph G, void (*visit)(Vertex V) ) {
    int mp[MaxVertices][MaxVertices] = {0},num = G -> NumOfVertices;
    int vis[MaxVertices] = {0};
    for(int i = 0;i < num;i ++) {
        PtrToVNode p = G -> Array[i];
        while(p) {
            mp[i][p -> Vert] = 1;
            p = p -> Next;
        }
    }
    for(int k = 0;k < num;k ++) {
        for(int i = 0;i < num;i ++) {
            for(int j = 0;j < num;j ++) {
                if(mp[i][k] && mp[k][j]) mp[i][j] = 1;
            }
        }
    }
    for(int i = 0;i < num;i ++) {
        if(vis[i]) continue;
        visit(i);
        vis[i] = 1;
        for(int j = 0;j < num;j ++) {
            if(!vis[j] && mp[i][j] && mp[j][i]) {
                vis[j] = 1;
                visit(j);
            }
        }
        putchar('\n');
    }
}

7-1 地下迷宫探索 (20分)

题目描述

假设有一个地下通道迷宫,它的通道都是直的,而通道所有交叉点(包括通道的端点)上都有一盏灯和一个开关。请问你如何从某个起点开始在迷宫中点亮所有的灯并回到起点?
在这里插入图片描述
输入格式:
输入第一行给出三个正整数,分别表示地下迷宫的节点数N(1<N≤1000,表示通道所有交叉点和端点)、边数M(≤3000,表示通道数)和探索起始节点编号S(节点从1到N编号)。随后的M行对应M条边(通道),每行给出一对正整数,分别是该条边直接连通的两个节点的编号。

输出格式:
若可以点亮所有节点的灯,则输出从S开始并以S结束的包含所有节点的序列,序列中相邻的节点一定有边(通道);否则虽然不能点亮所有节点的灯,但还是输出点亮部分灯的节点序列,最后输出0,此时表示迷宫不是连通图。

由于深度优先遍历的节点序列是不唯一的,为了使得输出具有唯一的结果,我们约定以节点小编号优先的次序访问(点灯)。在点亮所有可以点亮的灯后,以原路返回的方式回到起点。

输入样例1:
6 8 1
1 2
2 3
3 4
4 5
5 6
6 4
3 6
1 5
输出样例1:
1 2 3 4 5 6 5 4 3 2 1
输入样例2:
6 6 6
1 2
1 3
2 3
5 4
6 5
6 4
输出样例2:
6 4 5 4 6 0

代码实现

#include<bits/stdc++.h>
using namespace std;
#define MAX 0x3f3f3f3f;
int N, M, S;
vector<vector<int> > myMap;//使用邻接矩阵,如果使用邻接表需要插入排序
vector<bool> visited;
bool first = true;
int counter = 0;

void dfs(int start) {
	if (!first)
		cout << ' ';
	cout << start;
	first = false;
	
	counter++;
	visited[start] = true;
	for (int i = 1; i <= N; i++) {
		if (!visited[i] && myMap[start][i]) {
			dfs(i);
			cout << ' ' << start;
		}
	}
}

int main() {
	cin >> N >> M >> S;
	myMap.resize(N + 1, vector<int>(N + 1));
	visited.resize(N + 1);
	for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= N; j++) {
			myMap[i][j] = 0;
		}
	}
	for (int i = 1; i <= N; i++) {
		visited[i] = false;
	}
	int tmpStart, tmpEnd;
	for (int i = 0; i < M; i++) {
		cin >> tmpStart >> tmpEnd;
		myMap[tmpStart][tmpEnd] = myMap[tmpEnd][tmpStart] = 1;
	}
	dfs(S);
	if (counter < N) {
		cout << " 0";
	}
	return 0;
}

7-2 六度空间 (20分)

题目描述

“六度空间”理论又称作“六度分隔(Six Degrees of Separation)”理论。这个理论可以通俗地阐述为:“你和任何一个陌生人之间所间隔的人不会超过六个,也就是说,最多通过五个人你就能够认识任何一个陌生人。”如图1所示。
在这里插入图片描述
“六度空间”理论虽然得到广泛的认同,并且正在得到越来越多的应用。但是数十年来,试图验证这个理论始终是许多社会学家努力追求的目标。然而由于历史的原因,这样的研究具有太大的局限性和困难。随着当代人的联络主要依赖于电话、短信、微信以及因特网上即时通信等工具,能够体现社交网络关系的一手数据已经逐渐使得“六度空间”理论的验证成为可能。

假如给你一个社交网络图,请你对每个节点计算符合“六度空间”理论的结点占结点总数的百分比。

输入格式:
输入第1行给出两个正整数,分别表示社交网络图的结点数N(1<N≤103
​​ ,表示人数)、边数M(≤33×N,表示社交关系数)。随后的M行对应M条边,每行给出一对正整数,分别是该条边直接连通的两个结点的编号(节点从1到N编号)。

输出格式:
对每个结点输出与该结点距离不超过6的结点数占结点总数的百分比,精确到小数点后2位。每个结节点输出一行,格式为“结点编号:(空格)百分比%”。

输入样例:
10 9
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
9 10
输出样例:
1: 70.00%
2: 80.00%
3: 90.00%
4: 100.00%
5: 100.00%
6: 100.00%
7: 100.00%
8: 90.00%
9: 80.00%
10: 70.00%

代码实现

#include<bits/stdc++.h>
using namespace std;
#define MAX 0x3f3f3f3f;
int N, M;
vector<vector<int> >myMap;
vector<bool> visited;

void bfs(int start) {
	int counterOfPeople = 0;
	vector<int> current;
	vector<int> nextLevel;
	nextLevel.push_back(start);
	for (int i = 0; i < 6; i++) {
		current = nextLevel;
		nextLevel.clear();
		for (int j = 0; j < current.size(); j++) {
			for (int k = 0; k < myMap[current[j]].size(); k++) {
				if (!visited[myMap[current[j]][k]]) {
					visited[myMap[current[j]][k]] = true;
					counterOfPeople++;
					nextLevel.push_back(myMap[current[j]][k]);
				}
			}
		}
	}
	cout << start << ": " << fixed << setprecision(2) << (float)counterOfPeople / (float)N * 100 << '%' << endl;
}
//
//void dfs(int start,int counterOfLevel,int &counterOfPeople) {
//	if (counterOfLevel == 7)return;
//	
//	visited[start] = true;
//	counterOfPeople++;
//	counterOfLevel++;
//	for (int i = 0; i < myMap[start].size(); i++) {
//		if (!visited[myMap[start][i]]) {
//			dfs(myMap[start][i], counterOfLevel, counterOfPeople);
//		}
//	}
//}

int main() {
	cin >> N >> M;
	myMap.resize(N + 1);
	visited.resize(N + 1);
	int tmpStart, tmpEnd;
	for (int i = 0; i < M; i++) {
		cin >> tmpStart >> tmpEnd;
		myMap[tmpStart].push_back(tmpEnd);
		myMap[tmpEnd].push_back(tmpStart);
	}
	for (int i = 1; i <= N; i++) {
		for (int i = 1; i <= N; i++) {
			visited[i] = false;
		}
		int ansPeople = 0;
		bfs(i);
		//cout << i << ": " << fixed << setprecision(2) << (float)ansPeople / (float)N * 100 << '%' << endl;
	}
	return 0;
}

7-3 社交网络图中结点的“重要性”计算 (25分)

题目描述

在这里插入图片描述
输入样例:
9 14
1 2
1 3
1 4
2 3
3 4
4 5
4 6
5 6
5 7
5 8
6 7
6 8
7 8
7 9
3 3 4 9
输出样例:
Cc(3)=0.47
Cc(4)=0.62
Cc(9)=0.35

代码实现

#include<bits/stdc++.h>
using namespace std;
#define MAX 0x3f3f3f3f;
int N, M, K;
vector<vector<int> >table;

int minInSP(vector<int> SP, vector<bool> selected) {
	int res = -1, minValue = MAX;
	for (int i = 1; i <= N; i++) {
		if (!selected[i]&&SP[i] < minValue) {
			res = i;
			minValue = SP[i];
		}
	}
	return res;
}

float dijkstra(int start) {
	vector<bool> selected(N + 1);
	vector<int> shortestPath(N + 1);
	for (int i = 1; i <= N; i++) {
		selected[i] = false;
	}
	selected[start] = true;
	for (int i = 1; i <= N; i++) {
		if (table[start][i]) {
			shortestPath[i] = table[start][i];
		}
		else {
			shortestPath[i] = MAX;
		}
	}
	shortestPath[start] = 0;
	for (int i = 1; i < N; i++) {
		int min = minInSP(shortestPath,selected);
		if (min == -1) {
			return 0;
		}
		selected[min] = true;
		for (int j = 1; j <= N; j++) {
			if (!selected[j] && shortestPath[min] + table[min][j] < shortestPath[j]) {
				shortestPath[j] = shortestPath[min] + table[min][j];
			}
		}
	}
	/*for (int i = 1; i <= N; i++) {
		if (shortestPath[i] >= 0x3f3f3f3f) {
			return 0;
		}
	}*/
	float sum = 0;
	for (int i = 1; i <= N; i++) {
		if (i == start)continue;
		sum += shortestPath[i];
	}
	return (float)(N - 1) / sum;
}

int main() {
	cin >> N >> M;
	table.resize(N + 1, vector<int>(N + 1));
	for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= N; j++) {
			table[i][j] = MAX;
		}
	}
	int tmpStart, tmpEnd;
	for (int i = 0; i < M; i++) {
		cin >> tmpStart >> tmpEnd;
		table[tmpStart][tmpEnd] = table[tmpEnd][tmpStart] = 1;
	}
	cin >> K;
	int tmpK;
	for (int i = 0; i < K; i++) {
		cin >> tmpK;
		cout << "Cc(" << tmpK << ")=" <<fixed<<setprecision(2)<< dijkstra(tmpK) << endl;
	}
	return 0;
}

返回主目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值