欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

数据结构课设:神秘国度的爱情故事

程序员文章站 2022-04-11 07:55:44
...

神秘国度的爱情故事

题目要求:某个太空神秘国度中有很多美丽的小村,从太空中可以想见,小村间有路相连,更精确一点说,任意两村之间有且仅有一条路径。小村 A 中有位年轻人爱上了自己村里的美丽姑娘。每天早晨,姑娘都会去小村 B 里的面包房工作,傍晚 6 点回到家。年轻人终于决定要向姑娘表白,他打算在小村 C 等着姑娘路过的时候把爱慕说出来。问题是,他不能确定小村 B 是否在小村 A到小村 C 之间的路径上。你可以帮他解决这个问题吗?

输入 :第一行是村庄的个数M,接下来M-1行每行两个树Va,Vb表示哪些村庄是直接相连的,之后是提问次数N,接下来N行每行3个整数va,vb,vc,每组发问若vb在va与vc的必经之路上则输出"yes",否则输出"No".


分析

  • 题目中有个非常重要的信息:任意两个村之间有且仅有一条路径。
  • 这容易让我们联想到数据结构中的树结构,正好符合这个特征。
  • 但是结合情景,村庄之间并不存在树节点的父子关系,所以这个数据结构又是一个无向图,并且无环。
  • 再回来分析问题,可以分成两部分,第一是找到A村与C村的路径,第二是判断B村是否在这个路径上。
  • 最后我们在实现上可以选择邻接表作为存储数据的结构,结合广度优先搜索,从A点开始不断探索无向图,同时需要记录来路,方便之后回溯。BFS完成后则从C点开始沿着BFS的轨迹进行回溯,直至会带起点A。更具路径中是否出现B点即可判断结果。
  • 为了生成大量的测试数据,我们可以做一个函数,随机生成一个有数万个节点的无向连通图。然后再生成多个不重复的随机数。用重定向的方法将输入输出重定向到文件流,避免直接从控制台复制黏贴大量数据。
  • 为了让程序的思路更加清晰简洁,我们可以将这个神秘国度抽象成一个类,将构建村庄连接关系,判断三个村庄是否同一路径等方法以及保存村庄关系的邻接表封装起来。

以下是主要思路
数据结构课设:神秘国度的爱情故事
以下是类图
数据结构课设:神秘国度的爱情故事

生产输入数据的代码
//it function is used to create mock input data, useally ouput to an txt file
void createInputData(int dataNum, int requireNum){
	if (dataNum<3 || dataNum>maxn || requireNum <= 0 || requireNum > maxn) {
		puts("input data seen not right, please check!");
		return;
	}
	cout << "query" << endl;
	cout << dataNum - 1 << endl;

	//create an random array that each number would only appear once
	vector<int> tmpVec;
	for (int i = 0; i < dataNum; i++){	//note that the index of village is start from 0
		tmpVec.push_back(i);
	}
	srand(time(0));
	random_shuffle(tmpVec.begin(), tmpVec.end());

	//create an tree to simulate the connection of all village
	queue<int> tmpQue;
	int index = 0;
	tmpQue.push(tmpVec[index++]);
	int remain = dataNum - 1;	//the number of village taht still not join to the tree
	while (!tmpQue.empty()){
		int target = tmpQue.front();
		tmpQue.pop();
		int fork = (rand() % maxnFork) + 1;	//fork is the neighbor number of an village
		fork = min(fork, remain);
		remain -= fork;
		for (int i = 0; i < fork; i++){	//set neighbor for a village randomly
			int neib = tmpVec[index];	//the village that going to join to the tree
			printf("%d %d\n", target, tmpVec[index]);
			tmpQue.push(tmpVec[index++]);
		}
	}
	//create random data in query format
	cout << requireNum << endl;
	for (int i = 0; i < requireNum; i++){
		int a = rand() % dataNum;
		int b = rand() % dataNum;
		int c = rand() % dataNum;
		if (a == b || a == c || b == c){
			i--;
			continue;
		}
		printf("%d %d %d\n", a, b, c);
	}
}

神秘国度类代码
class  MysteryCountry {
private:
	vector<int> neighbor[maxn];		//a vector mean a village, it save their neighbor's index 
	int vilageNum;					//the numbers of vilages
	int lastVil[maxn];				//record the path when bfs
	bool isVisit[maxn];				//used when bfs, isVisit[i]=true mean village have been visted before
	queue<int> bfsQue;				//used when bfs, the front of the queue will be bfs fristly
	enum result{ can, cannot, error };	//the reuslt of judeg(), mean whether tha man can find the woman

	//connect two villages
	void connect(int i, int j){
		neighbor[i].push_back(j);
		neighbor[j].push_back(i);
	}
	//init isVisit and bfsQue before each query
	void clearTmp(){
		memset(isVisit, false, sizeof(isVisit));
		while (!bfsQue.empty()){
			bfsQue.pop();
		}
	}
	//init neighbor before each build
	void init(){
		for (int i = 0; i < maxn; i++){
			neighbor[i].clear();
		}
	}
	//judge if village b is on the way between a and c
	result judge(int a, int b, int c){
		clearTmp();
		bfsQue.push(a);
		//BFS to find the only path between a and c
		while (!bfsQue.empty()){
			int nowAt = bfsQue.front();
			bfsQue.pop();
			if (isVisit[nowAt]){
				continue;
			}
			isVisit[nowAt] = true;
			for (int i = 0; i < neighbor[nowAt].size(); i++){
				int tnb = neighbor[nowAt][i];
				if (isVisit[tnb]){
					continue;
				}
				//cout << nowAt << "---------" << tnb << endl;
				if (tnb == nowAt || tnb == a){		//cycle 
					return error;
				}
				lastVil[tnb] = nowAt;
				if (tnb == c) {		//find the path succeed
					goto tag;
				}
				bfsQue.push(tnb);
			}
		}
	tag:
		//walk back and find whether village b is in the path
		int nowat = c;
		while (nowat != a){
			nowat = lastVil[nowat];
			if (nowat == b) return can;
		}
		return cannot;
	}
public:
	//get data and build an tree
	void build(){
		init();
		scanf("%d", &vilageNum);
		int va, vb;
		for (int i = 0; i < vilageNum; i++){
			scanf("%d %d", &va, &vb);
			connect(va, vb);
		}
	}
	//answer the quesions
	void answer(){
		int query = 0;
		scanf("%d", &query);
		while (query--){
			int va, vb, vc;
			scanf("%d %d %d", &va, &vb, &vc);
			//printf("%d %d %d   :  ", va, vb, vc);
			result res = judge(va, vb, vc);
			switch (res){
			case can:
				printf("can\n");
				break;
			case cannot:
				printf("can not \n");
				break;
			case error:
				printf("given data might be worng!\n");
				break;
			}
		}
	}
	//printf the tree
	void traverse(){
		memset(isVisit, false, sizeof(isVisit));
		queue<int> tmpQue;
		tmpQue.push(0);
		while (!tmpQue.empty()){
			int head = tmpQue.front();
			tmpQue.pop();
			isVisit[head] = true;
			for (int i = 0; i < neighbor[head].size(); i++){
				int tnb = neighbor[head][i];	//temp neighbor 
				printf("%d---%d\n", head, tnb);
				if (!isVisit[tnb]) {
					tmpQue.push(tnb);
				}
			}
		}
	}
};

测试结果

数据结构课设:神秘国度的爱情故事