2021-02-20

北航机试 2019 机场送旅客

先输入n(n < 10000),代表有n组数据,接下来n行每行输入四个数,第一个输入为分支节点,后三个输入为其三个孩子节点(-1代表子节点不存在)。输入的n行中第一行第一个节点为根节点,作为起点。保证输入的结点至多有一个父节点。
然后输入m(m < n),接下来输入m个客户,每行输入他的目标(叶节点)和优先级(数字越小越优先,保证不会有相同优先级),要求从起点出发,按照优先级把客户送到目标节点,然后从该节点继续运送下一个客户至其目标(叶节点),全部运送完毕后返回起点,记录每段经过的路径,并输出。输入的数据规模不会超过int型变量范围。

题目来自《北航机试复习指南 》By 慕戈云子

题目大意:一棵树,孩子没有先后次序,给你一些叶子结点,先从根到第一个叶子,然后从第一个叶子到第二个,以此类推,到达最后一个叶子后,返回根。乍一看,有点像树的前序遍历,但是其实是无法用一次前序遍历完成所有路径的输出的,因为叶子的访问顺序是给定的,而不是树本身的顺序。
所以还是老老实实的求出每一段路径来。
在树里面求任意两个节点的最短路径应该是有些高效算法的,比如用LCA(Lowest Common Ancestor),但是考虑到数据量不大,就直接DFS暴力搜索了。
枚举每次路径的源点和终点,DFS搜之,输出路径,记得除了第一条路径外,不要输出起点,因为上一条路径的终点就是本路径的起点,不要重复了。

至于节点的编号问题,北航的几道题目都没有给出节点编号,需要自己把输入的数据转为编号。用STL即可,没有的话,顺序表搜索也可以。

#include <cstdio>
#include <vector>
#include <algorithm>
#include <map> // 节点转下标。 
using namespace std;

#define MAXN 505 // 节点上限。
// N行数据。 
int N;
vector<int> Adj[MAXN]; 
bool vis[MAXN];
vector<int> Path;

#define MAXM 105
int M;

int index; // 分配节点下标。
map<int,int> mp; // 数字转下标。 
int mp2[MAXN]; // 下标转数字。 

/* run this program using the console pauser or add your own getch, system("pause") or input loop */

/*
从根出发,到达A,从A出发,达到B,以此类推,达到最后一个旅客后,
返回根,于是有M个旅客,输出M+1行,每行是一段路。
注意,每段路,除了第一段外,不要输出起点,但是每段路都要输出终点。 
把三叉树记录为无向图,DFS找每段路。

每次DFS的复杂度是遍历全图,共遍历M+1次,复杂度为O(M*(E+V))。 
*/
// 是否打印源点,除了第一段路外,都不打印源点。 
int start=0;

void DFS(int s, int t) {
	if (s == t) {
		Path.push_back(s);
		// 输出路径。
		// 注意顺序打印。 
		for (int i=start;i<Path.size();++i) {
			int v=Path[i];
			int ans=mp2[v];
			printf("%d%s", ans,i==Path.size()-1?"\n":" ");
		}
		Path.pop_back();
		return;
	}
	vis[s]=true;
	Path.push_back(s);
	for (int i=0;i<Adj[s].size();++i) {
		int v=Adj[s][i];
		if (!vis[v]) {
			DFS(v, t);
		}
	}
	Path.pop_back();
	vis[s]=false;
}

void PrintPath(int s, int t) {
	fill(vis,vis+MAXN,false);
	Path.clear();
	DFS(s, t);
	if (!start) {
		start=1;
	}
}


int Map(int x) {
	if (mp.count(x)) {
		return mp[x];
	} else {
		mp[x]=index;
		mp2[index]=x;
		return index++;
	}
}

struct Person {
	int v; // 目的地,叶子节点。
	int prio; // 优先级。 
	friend bool operator<(Person a, Person b) {
		return a.prio < b.prio;
	}
} P[MAXM]; // 旅客 

// 记录树根。 
int Root;

int main(int argc, char** argv) {
	scanf("%d",&N);
	for (int i=0;i<N;++i) {
		// 建图。
		int u; // 父节点。
		scanf("%d",&u);
		u=Map(u);
		if (!i) {
			// 保证第一行第一个节点是根。 
			Root=u;
		}
		int T=3;
		while (T--) {
			// 三个孩子。
			int v;
			scanf("%d",&v);
			if (v == -1) {
				continue;
			}
			v=Map(v);
			Adj[u].push_back(v);
			Adj[v].push_back(u);
		} 
	}
	scanf("%d", &M);
	for (int i=0;i<M;++i) {
		int v;
		scanf("%d%d",  &v,&P[i].prio);
		P[i].v=Map(v);
	}
	sort(P, P+M);
	for (int i=0;i<M;++i) {
		int u, v;
		if (i==0) {
			u=Root;
			v=P[i].v;
		} else {
			u=P[i-1].v;
			v=P[i].v;
		}
		PrintPath(u, v);
	}
	// 回到根。 
	PrintPath(P[M-1].v, Root);
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值