特殊路径统计(C++)

[问题描述]

给定一颗有N个节点(编号为1-N)的树。

两个节点a,b(a<b)之间的简单路径上所有节点编号i均在a,b之间(a≤i≤b),则该路径可标记为特殊路径。

试统计树上一共有多少条特殊路径。

[输入格式]

+ 第一行包含一个整数N,代表节点数

+ 第二行包含N个整数p1,p2,…,pn,代表每个节点的父节点编号。若pi=0,则该节点为树的根节点

[输出数据]

输出树上一共有多少条特殊路径

[补充说明]

+ 0≤pi≤N

+ 有且仅有一个pi=0

+ 输入的图是一棵树

[样例1]

输入:

7

0 5 5 1 4 1 4

输出:

10

[样例2]

输入:

5

2 3 0 2 2

输出:

7

其中样例1的图形示例:

 

 分析:

首先,它是一棵树,这个条件可以保证图中任意两点之间都是连通的。然后,我们要把它作为一个无向连通图来处理。这里,我们以邻接表为基本的数据结构。要找到所有特殊路径,从本质上看,就是让我们对图进行遍历。我们以从点1出发为例,每遍历到一个点,只有当这个点比路径上的初始点大时,我们才把它加入路径,另外,把它与这条路径上的最大值作比较,若新增的点较大,则表示一条新的特殊路径的产生,输出这条路径上的首尾点,并更新这条路上的最大点。这样,我们依次从1开始遍历(1是起始点),就可以找到所有特殊路径。关于如何保存最大点的问题,可以给结构体增加一个变量,保存当前结点所在路径上的最大值。

程序代码: 

# include <iostream>
# include <queue>
# define SIZE 20
# define NEWSIZE 20
using namespace std;
//以邻接表为基本数据结构
typedef struct ArcNode {      //边的结点结构类型
	int adjvex;               //该边的终点编号
	struct ArcNode* nextarc;  //指向下一条边的指针
}ArcNode;
typedef struct VexNode {  //顶点结构
	int data;             //当前结点的值
	int maxdata;          //当前结点所在路径上的最大值
	ArcNode* firstarc;    //指向第一条与该顶点有关的边的指针
}VexNode;
typedef struct Graph {    //邻接表结构类型
	VexNode* VNode;       //定义邻接表
	int vexnum;           //顶点个数
	int size;             //邻接表的大小
}Graph;

int sum = 0;  //特殊路径总数
int* Visit;   //辅助数组,标记点是否已被访问过
void Create(Graph& G);   //创建
void Find(Graph G);      //寻找全部特殊路径

int main()
{
	Graph G;
	Create(G);     //创建
	cout << endl;
	Find(G);       //寻找全部特殊路径
	cout << "特殊路径总数:" << sum << endl;
	return 0;
}

void Create(Graph& G)   //创建
{
	cin >> G.vexnum;   //输入顶点个数
	G.VNode = (VexNode*)malloc(SIZE * sizeof(VexNode));
	G.size = SIZE;
	while (G.size < G.vexnum) {
		//根据点的个数动态分配空间
		G.VNode = (VexNode*)realloc(G.VNode, (G.size + NEWSIZE) * sizeof(VexNode));
		G.size += NEWSIZE;
	}
	Visit = (int*)malloc((G.size + 10) * sizeof(int));
	for (int i = 1; i <= G.vexnum; i++) {
		G.VNode[i].data = i;
		G.VNode[i].firstarc = NULL;  //邻接表初始化,所有单向链表均为空表
	}
	int a;
	ArcNode* p, * q;
	for (int i = 1; i <= G.vexnum; i++) {
		cin >> a;
		if (a > 0) {
			p = (ArcNode*)malloc(sizeof(ArcNode));  //创建一个用于存放当前边的结点p
			p->nextarc = NULL;
			p->adjvex = i;
			q = G.VNode[a].firstarc;
			if (q == NULL) {
				G.VNode[a].firstarc = p;
			}
			else {
				while (q->nextarc != NULL) {
					q = q->nextarc;
				}
				q->nextarc = p;
			}
			p = (ArcNode*)malloc(sizeof(ArcNode));  //再创建一个表示对称边的结点p
			p->nextarc = NULL;
			p->adjvex = a;
			q = G.VNode[i].firstarc;
			if (q == NULL) {
				G.VNode[i].firstarc = p;
			}
			else {
				while (q->nextarc != NULL) {
					q = q->nextarc;
				}
				q->nextarc = p;
			}
		}
	}
}

void Search_BFS(Graph G, int n)   //以n为起点,BFS寻找特殊路径
{
	queue<int>Q;
	Visit[n] = 1;             //将起始点标记为已被访问过的状态
	Q.push(n);                //起始点入队
	int m, t = 0;
	while (!Q.empty()) {
		m = Q.front();        //队头元素出队
		Q.pop();
		ArcNode* p = G.VNode[m].firstarc;
		while (p) {
			//遍历当前队头结点的所有邻接点,若邻接点已被访问过,则p继续向后遍历
			//否则入队,并标记为已访问状态,防止重复入队
			if (!Visit[p->adjvex] && p->adjvex >= n) {
				if (p->adjvex >= G.VNode[m].maxdata) {
					//如果新结点的值大于当前路径上的最大值
					cout << n << "-" << p->adjvex << " ";
					sum++;
					t = 1;
				}
				else {
					G.VNode[p->adjvex].maxdata = G.VNode[m].maxdata;
				}
				Visit[p->adjvex] = 1;
				Q.push(p->adjvex);
			}
			p = p->nextarc;
		}
	}
	if (t) {
		cout << endl;
	}
}

void Find(Graph G)      //寻找特殊路径
{
	for (int i = 1; i < G.vexnum; i++){
		for (int i = 1; i <= G.vexnum; i++){
			Visit[i] = 0;    //辅助数组Visit初始化
			G.VNode[i].maxdata = i;  //当前结点所在路径上的最大值初始化
		}
		Search_BFS(G, i);
	}
}

运行结果: 

7
0 5 5 1 4 1 4

1-4 1-6 1-5 1-7
2-5 2-7
3-5 3-7
4-5 4-7
特殊路径总数:10

这道题涉及到了树和图,以图为主。主要考查了图的建立和图的遍历。这里,我使用了邻接表作为基本数据结构,采用了BFS的遍历方法(关于邻接表和BFS在我之前的文章里讲过,这里就不再多说了)。总体上的时间复杂度约为O(n^2)。

以上是我对这道题的看法,很高兴能与大家分享。

  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
如果需要生成不同路径C++代码,可以考虑使用以下方法: 1. 使用条件编译:在代码中添加条件编译指令,据不同的条件生成不同的代码。例如,可以使用`#if`和`#else`指令来判断条件,生成不同的代码。例如: ```c++ #if defined(PATH1) // Path 1 code here #elif defined(PATH2) // Path 2 code here #else // Default code here #endif ``` 在编译时,可以通过定义不同的宏来选择不同的路径,例如: ```bash g++ -D PATH1 source.cpp -o output1 g++ -D PATH2 source.cpp -o output2 ``` 2. 使用代码生成器:编写一个代码生成器程序,据不同的条件生成不同的代码文件。例如,可以使用Python编写一个代码生成器,读取输入文件,据不同条件生成不同的代码文件。例如: ```python if condition1: with open('path1.cpp', 'w') as f: f.write('Path 1 code here') elif condition2: with open('path2.cpp', 'w') as f: f.write('Path 2 code here') else: with open('default.cpp', 'w') as f: f.write('Default code here') ``` 3. 手动编写代码:据需要生成的不同路径的代码,手动编写不同的代码文件。例如,在不同的目录下创建不同的代码文件,分别实现不同的功能。在编译时,选择需要的代码文件进行编译。例如: ```bash g++ path1/source.cpp -o output1 g++ path2/source.cpp -o output2 ``` 总之,生成不同路径C++代码需要考虑到不同的情况和需求,可以据实际情况选择不同的方法进行实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值