UVa OJ 125 - Numbering Paths (路径计数)

本文探讨了一种基于深度优先搜索的算法,用于解决城市单行线路网中的路径计数问题,尤其是在存在环的情况下如何有效计算路径数量。文章详细介绍了算法的具体实现过程,并提供了一个完整的示例程序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Background
背景

Problems that process input and generate a simple "yes" or "no" answer are called decision problems. One class of decision problems, the NP-complete problems, are not amenable to general efficient solutions. Other problems may be simple as decision problems, but enumerating all possible "yes" answers may be very difficult (or at least time-consuming).
仅由简单的“是”或“否”来回答的问题被称作决策问题,其中属于NP完全的一类决策问题是难以快速求解的。另一些非决策的NP完全问题的答案可能与决策问题一样简单,但枚举所有“是”的答案则会非常困难(至少要花大量的时间)。

This problem involves determining the number of routes available to an emergency vehicle operating in a city of one-way streets.
本问题是要来计算:在一个所有街道都为单行线的城市中,一辆急救车可以通行的路径的数量。

 

The Problem
问题

Given the intersections connected by one-way streets in a city, you are to write a program that determines the number of different routes between each intersection. A route is a sequence of one-way streets connecting two intersections.
给定城市中一组由单行线相连的路口,你要写一个程序来计算出各路口间有多少条不同的路径。一条路径由一系列将两个路口连通的单行线组成。

Intersections are identified by non-negative integers. A one-way street is specified by a pair of intersections. For example, j k indicates that there is a one-way street from intersection j to intersection k. Note that two-way streets can be modeled by specifying two one-way streets: j k and k j.
各路口均由非负整数标记,每条单行线由两个路口表示,如j k表示存在一条单行线从路口j到路口k。注意双行线可以由两条单行线模拟,比如:j k和k j。

Consider a city of four intersections connected by the following one-way streets:
观查下面一个拥有四个路口并由单行线相互连通的城市:

0  1
0  2
1  2
2  3

There is one route from intersection 0 to 1, two routes from 0 to 2 (the routes are 0->1->2 and 0->2 ), two routes from 0 to 3, one route from 1 to 2, one route from 1 to 3, one route from 2 to 3, and no other routes.
在0路口到1路口之间只有1条路径,0到2之间有两条路径(0->1->2和0->2),由0到3有两条。1到3有一条,2到3有1条,这些就是全部的路径了。

It is possible for an infinite number of different routes to exist. For example if the intersections above are augmented by the street 3 2 , there is still only one route from 0 to 1, but there are infinitely many different routes from 0 to 2. This is because the street from 2 to 3 and back to 2 can be repeated yielding a different sequence of streets and hence a different route. Thus the route 0->2->3->2->3->2 is a different route than 0->2->3->2.
有可能不同的路径有无数条。比如对于上面讨论过路口2和3,增加一条单行线3 2,此时由0到1仍只有1条路径,但从0到2就有无数条不同的路径。这是由于从2到3后又可以折返回到2,每次往复折返都会产生一条新的不同路径。比如0->2->3->2->3->2与0->2->3->2就是两条不同的路径。

 

The Input
输入

The input is a sequence of city specifications. Each specification begins with the number of one-way streets in the city followed by that many one-way streets given as pairs of intersections. Each pair j k represents a one-way street from intersection j to intersection k. In all cities, intersections are numbered sequentially from 0 to the "largest" intersection. All integers in the input are separated by whitespace. The input is terminated by end-of-file.
输入为一系列城市的定义。每个定义最前面的数据是城市中的单行线数量,后面跟着该数量的单行线定义,每个单行线由一对由整数标记的路口给出。每一对整数j k表示一条单行线由路口j到k。在所有的城市中,路口的编号均由0到“最大”的一个路口,各不相同。输入的所有整数都由空格隔开。输入由EOF表示结束。

There will never be a one-way street from an intersection to itself. No city will have more than 30 intersections.
不存在起点和终点路口相同的单行线。每个城市最多30个路口。

 

The Output
输出

For each city specification, a square matrix of the number of different routes from intersection j to intersection k is printed. If the matrix is denoted M, then M[j][k] is the number of different routes from intersection j to intersection k. The matrix M should be printed in row-major order, one row per line. Each matrix should be preceded by the string "matrix for city k" (with k appropriately instantiated, beginning with 0).
对于每一个城市的定义,要按矩阵的格式打印出每一对路口j和i之间存在的不同路径的数量。设该矩阵为M,那么M[j][k]即为从路口j到路口k之间不同路径的数量。矩阵M应该按行先顺序打印,矩阵的每行都独占一行输出。每个矩阵前都应由一行字符串“matrix for city k”作为开头(k表示给出的矩阵顺序编号,由0开始)。

If there are an infinite number of different paths between two intersections a -1 should be printed. DO NOT worry about justifying and aligning the output of each matrix. All entries in a row should be separated by whitespace.
如果在两个口之间存在无数量不同的路径,则应打印出-1。不要刻意的使矩阵对齐输出,行中的每个元素用空格隔开即可。

 

Sample Input
输入示例

7 0 1 0 2 0 4 2 4 2 3 3 1 4 3
5
0 2
0 1 1 5 2 5 2 1
9
0 1 0 2 0 3
0 4 1 4 2 1
2 0
3 0
3 1

 

Sample Output
输出示例

matrix for city 0
0 4 1 3 2
0 0 0 0 0
0 2 0 2 1
0 1 0 0 0
0 1 0 1 0
matrix for city 1
0 2 1 0 0 3
0 0 0 0 0 1
0 1 0 0 0 2
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
matrix for city 2
-1 -1 -1 -1 -1
0 0 0 0 1
-1 -1 -1 -1 -1
-1 -1 -1 -1 -1
0 0 0 0 0

 

Analysis
分析

有些难度题目,值得推荐。题目也非常有趣,建议您在仔细的读题并认真的思考后再来看分析和解答,最好能亲自动手做一下。

它看似需要用图论的某种算法来解决,事实上这道题的主要是靠深度遍例(DFS)。对于一个无环图来说,用深度遍例找出所有路径的算法是典型的,依靠邻接表即可完成,但这道题的主要难度就在环上。要先找出所有的环,就得在递归的过程中记录由起点到达当前节点的路径。这样当进入下一级节点时,只需在路径中查找与之相同的节点,如果找到即可判定存在环。若下一级节点与当前路径构成环,就不能继续向下一级遍例,否则将进入死循环。如果没有构成环,就为起点到达当前节点的路径数量加1。

每当发现一个环时,应该将环上所有的路口都进行标记,因为由这些路口出发的所有路径都可以有无数条(至少可以先绕着环转一圈回到起点)。在深度遍例结束后就得到了所有在环上的路口以及到由起点路口到达各路口的路径数量。

这些路径中有些是不满足条件的,因为可能“穿过”一些环,现在就需要把这些路径找出来。显然,如果到达的路口就在环上,那么路径一定为无数条,而且,能通过这些环上的路口到达的其它路口的路径也为无数条。如果路径上所有的点都不在环上,则为满足条件的可达路径。

我想这道题应该还有别的更好的思路,还请各位老师给予指导。下面的代码比较长,也没有经过足够的优化,可能看起来会比较乱,望见谅!

 

Solution
解答

#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
//常量,表示可能出现的路口总数
const int nTotal = 30;
//用来存储结果的矩阵
int aMat[nTotal][nTotal];
//邻接表
vector<int> AdjTbl[nTotal];
//最大路口编号
int nMaxIs;
//深度遍例,每次找出由路径最后一个路口可达的所有路口
void SearchRoad(vector<int> &Path) {
	//为加快运算,方便编码,为当前路口(路径末端)的邻接表设临时变量
	vector<int> &Adj = AdjTbl[Path.back()];
	//循环遍例当前路口的每一个相邻路口
	for (vector<int>::iterator iTo = Adj.begin(), i; iTo != Adj.end(); ++iTo) {
		//从路径首个路口开始向后寻找可达路口,即检查是否已出现环
		for (i = Path.begin(); i != Path.end() && *i != *iTo; ++i);
		//如果找到环
		if (i != Path.end()) {
			//环中每个路口到它邻接的路口都有无数条路径
			for (vector<int>::iterator j = i; j != Path.end(); ++j) {
				aMat[*j][*j] = -1;
			}
			//继续下一个节点,避免对环进行深度遍例
			continue;
		} //若与路径中任一个路口都不直接构成环,可继续向下一级遍例
		//并累计到达下一级路口的次数
		++aMat[Path.front()][*iTo];
		//将下一级路口加入路径
		Path.push_back(*iTo);
		//继续向下执行深度遍例
		SearchRoad(Path);
		//本节点及其子树都遍例完毕,恢复原路径
		Path.pop_back();
	}
}
//主函数
int main(void) {
	//循环处理输入的每一个城市交通数据
	for (int nWayCnt, nCityCnt = 0; cin >> nWayCnt; ++nCityCnt) {
		//清空结果矩阵和邻接表
		memset(aMat, 0, sizeof(aMat));
		for (int i = 0; i < nTotal; AdjTbl[i++].clear());
		nMaxIs = 0;
		//读入所有单行线数据,并统计出现的最大路口编号
		for (int i = 0, nFrom, nTo; i < nWayCnt; ++i) {
			cin >> nFrom >> nTo;
			AdjTbl[nFrom].push_back(nTo);
			nMaxIs = max(max(nFrom, nTo), nMaxIs);
		}
		//查找从每个路口可到达的所有路口并发现所有在环上的路口
		for (int nRoot = 0; nRoot <= nMaxIs; ++nRoot) {
			vector<int> Path(1, nRoot);
			SearchRoad(Path);
		}
		//处理图中的环
		for (int i = 0; i <= nMaxIs; ++i) {
			//如果路口本身就在环上,则所有可达路口都有无数条路径
			if (aMat[i][i] == -1) {
				//为其所有可达路口的路径赋值为-1
				for (int j = 0; j <= nMaxIs; ++j) {
					aMat[i][j] = aMat[i][j] != 0 ? -1 : aMat[i][j];
				}
				continue;
			}
			//如果路口不在环上,则查找其所有可达路口是否在环上
			for (int j = 0; j <= nMaxIs; ++j) {
				//若可达路口j在环上,那么i至j有无数条路径
				if (aMat[i][j] != 0 && aMat[j][j] == -1) {
					//显然j可达的所有路口k,i也可达,且路径同样无数
					for (int k = 0; k <= nMaxIs; ++k) {
						aMat[i][k] = (aMat[j][k] != 0) ? -1 : aMat[i][k];
					}
				}
			}
		}
		//按要求输出矩阵结果
		cout << "matrix for city " << nCityCnt << endl;
		for (int i = 0, j; i <= nMaxIs; ++i) {
			//避免在行尾多出空格
			for (j = 0; j < nMaxIs; cout << aMat[i][j++] << ' ');
			cout << aMat[i][j] << endl;
		}
	}
	return 0;
}

转载于:https://www.cnblogs.com/devymex/archive/2010/08/17/1801126.html

基于数据挖掘的音乐推荐系统设计与实现 需要一个代码说明,不需要论文 采用python语言,django框架,mysql数据库开发 编程环境:pycharm,mysql8.0 系统分为前台+后台模式开发 网站前台: 用户注册, 登录 搜索音乐,音乐欣赏(可以在线进行播放) 用户登陆时选择相关感兴趣的音乐风格 音乐收藏 音乐推荐算法:(重点) 本课题需要大量用户行为(如播放记录、收藏列表)、音乐特征(如音频特征、歌曲元数据)等数据 (1)根据用户之间相似性或关联性,给一个用户推荐与其相似或有关联的其他用户所感兴趣的音乐; (2)根据音乐之间的相似性或关联性,给一个用户推荐与其感兴趣的音乐相似或有关联的其他音乐。 基于用户的推荐和基于物品的推荐 其中基于用户的推荐是基于用户的相似度找出相似相似用户,然后向目标用户推荐其相似用户喜欢的东西(和你类似的人也喜欢**东西); 而基于物品的推荐是基于物品的相似度找出相似的物品做推荐(喜欢该音乐的人还喜欢了**音乐); 管理员 管理员信息管理 注册用户管理,审核 音乐爬虫(爬虫方式爬取网站音乐数据) 音乐信息管理(上传歌曲MP3,以便前台播放) 音乐收藏管理 用户 用户资料修改 我的音乐收藏 完整前后端源码,部署后可正常运行! 环境说明 开发语言:python后端 python版本:3.7 数据库:mysql 5.7+ 数据库工具:Navicat11+ 开发软件:pycharm
MPU6050是一款广泛应用在无人机、机器人和运动设备中的六轴姿态传感器,它集成了三轴陀螺仪和三轴加速度计。这款传感器能够实时监测并提供设备的角速度和线性加速度数据,对于理解物体的动态运动状态至关重要。在Arduino平台上,通过特定的库文件可以方便地与MPU6050进行通信,获取并解析传感器数据。 `MPU6050.cpp`和`MPU6050.h`是Arduino库的关键组成部分。`MPU6050.h`是头文件,包含了定义传感器接口和函数声明。它定义了类`MPU6050`,该类包含了初始化传感器、读取数据等方法。例如,`begin()`函数用于设置传感器的工作模式和I2C地址,`getAcceleration()`和`getGyroscope()`则分别用于获取加速度和角速度数据。 在Arduino项目中,首先需要包含`MPU6050.h`头文件,然后创建`MPU6050`对象,并调用`begin()`函数初始化传感器。之后,可以通过循环调用`getAcceleration()`和`getGyroscope()`来不断更新传感器读数。为了处理这些原始数据,通常还需要进行校准和滤波,以消除噪声和漂移。 I2C通信协议是MPU6050与Arduino交互的基础,它是一种低引脚数的串行通信协议,允许多个设备共享一对数据线。Arduino板上的Wire库提供了I2C通信的底层支持,使得用户无需深入了解通信细节,就能方便地与MPU6050交互。 MPU6050传感器的数据包括加速度(X、Y、Z轴)和角速度(同样为X、Y、Z轴)。加速度数据可以用来计算物体的静态位置和动态运动,而角速度数据则能反映物体转动的速度。结合这两个数据,可以进一步计算出物体的姿态(如角度和角速度变化)。 在嵌入式开发领域,特别是使用STM32微控制器时,也可以找到类似的库来驱动MPU6050。STM32通常具有更强大的处理能力和更多的GPIO口,可以实现更复杂的控制算法。然而,基本的传感器操作流程和数据处理原理与Arduino平台相似。 在实际应用中,除了基本的传感器读取,还可能涉及到温度补偿、低功耗模式设置、DMP(数字运动处理器)功能的利用等高级特性。DMP可以帮助处理传感器数据,实现更高级的运动估计,减轻主控制器的计算负担。 MPU6050是一个强大的六轴传感器,广泛应用于各种需要实时运动追踪的项目中。通过 Arduino 或 STM32 的库文件,开发者可以轻松地与传感器交互,获取并处理数据,实现各种创新应用。博客和其他开源资源是学习和解决问题的重要途径,通过这些资源,开发者可以获得关于MPU6050的详细信息和实践指南
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值