蓝桥杯2018决赛 版本分支


蓝桥杯2018决赛 版本分支

【题目描述】

小明负责维护公司一个奇怪的项目。这个项目的代码一直在不断分支(branch)但是从未发生过合并(merge)。
现在这个项目的代码一共有N个版本,编号1~N,其中1号版本是最初的版本。
除了1号版本之外,其他版本的代码都恰好有一个直接的父版本;即这N个版本形成了一棵以1为根的树形结构。
如下图就是一个可能的版本树:
1
/ \
2 3
| / \
5 4 6
现在小明需要经常检查版本x是不是版本y的祖先版本。你能帮助小明吗?

【输入格式】

第一行包含两个整数N和Q,代表版本总数和查询总数。
以下N-1行,每行包含2个整数u和v,代表版本u是版本v的直接父版本。
再之后Q行,每行包含2个整数x和y,代表询问版本x是不是版本y的祖先版本。
对于30%的数据,1 <= N <= 1000 1 <= Q <= 1000
对于100%的数据,1 <= N <= 100000 1 <= Q <= 100000

【输出格式】

对于每个询问,输出YES或NO代表x是否是y的祖先。

【样例输入】

6 5
1 2
1 3
2 5
3 6
3 4
1 1
1 4
2 6
5 2
6 4

【样例输出】

YES
YES
NO
NO
NO


测试网址http://oj.ecustacm.cn/problem.php?id=1400


个人解法

【解法一】

思路解析:使用了最近公共祖先LCA算法+倍增思想。(暂未做思路解析,有需要再补)

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.LinkedList;
import java.util.Queue;

public class _版本分支 {

	static int n, q, tot = 0, p = 17 + 1, N = (int) 1e5 + 5;
	static int[][] parents;
	static int[] head, depth;
	static Node[] ee;

	public static void main(String[] args) throws IOException {
		InputReader sc = new InputReader(System.in);
		n = sc.nextInt();
		q = sc.nextInt();

		parents = new int[N][p];
		head = new int[N];
		ee = new Node[N];
		depth = new int[N];

		for (int i = 1; i < n; ++i) {
			int u = sc.nextInt(), v = sc.nextInt();
			parents[v][0] = u;
			add(u, v);
		}

		build(1);

		while (q-- > 0) {
			int x = sc.nextInt(), y = sc.nextInt();
			int p = lca(x, y);
			if (p == x)
				System.out.println("YES");
			else
				System.out.println("NO");
		}

	}

	public static int lca(int x, int y) {
		if (depth[x] < depth[y]) {
			int t = x;
			x = y;
			y = t;
		}
		for (int i = p - 1; i >= 0; --i) {
			if (depth[parents[x][i]] >= depth[y])
				x = parents[x][i];
		}
		if (x == y)
			return x;
		for (int i = p - 1; i >= 0; --i) {
			if (parents[x][i] != parents[y][i]) {
				x = parents[x][i];
				y = parents[y][i];
			}
		}
		return parents[x][0];
	}

	public static void build(int start) {
		depth[start] = 1;
		int[] que = new int[N];
		int hh = 0, tt = 0;
		que[hh] = start;
		while (hh <= tt) {
			int p = que[hh++];
			for (int i = head[p]; i != 0; i = ee[i].next) {
				int t = ee[i].to;
				depth[t] = depth[p] + 1;
				for (int k = 1; k < p; ++k)
					parents[t][k] = parents[parents[t][k - 1]][k - 1];
				que[++tt] = t;
			}
		}
	}

	public static void add(int from, int to) {
		ee[++tot] = new Node(from, to, head[from]);
		head[from] = tot;
	}

	static class Node {
		int from, to, next;
		
		public Node(int from, int to, int next) {
			this.from = from;
			this.to = to;
			this.next = next;
		}
	}

	static class InputReader {
		StreamTokenizer tokenzier;

		public InputReader(InputStream stream) {
			tokenzier = new StreamTokenizer(new BufferedReader(new InputStreamReader(stream)));
			tokenzier.ordinaryChars(33, 126);
			tokenzier.wordChars(33, 126);
		}

		public String next() throws IOException {
			tokenzier.nextToken();
			return tokenzier.sval;
		}

		public int nextInt() throws IOException {
			return Integer.parseInt(next());
		}
	}

}
  • 附加拓展
public static void build(int start) {
	depth[start] = 1;
	Queue<Integer> que = new LinkedList<Integer>();
	que.add(start);
	while (!que.isEmpty()) {
		int p = que.poll();
		for (int i = head[p]; i != 0; i = ee[i].next) {
			int t = ee[i].to;
			depth[t] = depth[p] + 1;
			for (int j = 1; j < p; ++j)
				parents[t][j] = parents[parents[t][j - 1]][j - 1];
			que.add(t);
		}
	}
}

这种写法对于java来说容易报运行错误,当版本1是其他所有版本的直接父版本时,队列就有可能一下子放入100000-1个元素,会导致爆栈。 对于不清楚入队次数时,可以使用队列;而清楚入队次数时,使用上面第一种更适合。 同理对于dfs版的build也是会存在报栈的可能。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值