首页 > 试题广场 >

神秘的苹果树

[编程题]神秘的苹果树
  • 热度指数:4133 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解

小团找到一颗有n个节点的苹果树,以1号节点为根,且每个节点都有一个苹果,苹果都有一个颜色,但是这棵树被施加了咒术,这使得小团只能从某一个节点的子树中选取某一种颜色的拿。小团想要拿到数量最多的那种颜色的所有苹果,请帮帮她。每次她会指定一个节点t,如果小团只能从节点t的子树中选取某一种颜色的苹果,选取什么颜色能拿到最多的苹果?如果有多种颜色都可以拿同样多的苹果,输出颜色编号最小的那个对应的编号。

节点x的子树定义为所有将x当作祖先的节点,x也视为x的子树的一部分。


输入描述:

第一行一个正整数n表示这颗树上节点的个数。

接下来n-1行,每行两个正整数x­­i,yi,表示树上第i条边连接的两个节点。

接下来一行n个正整数c­i,分别表示从1~n号节点上的苹果的颜色。

接下来一行一个正整数q,表示接下来有q次独立的询问。

接下来q行,每行一个正整数t表示询问:如果小团只能从节点t的子树中选取某一种颜色的苹果,选取什么颜色能拿到最多的苹果?如果有多种颜色都可以拿同样多的苹果,输出颜色编号最小的那个对应的编号。

      

对于100%的数据n≤5000, 1≤xi,yi,t≤n, ci≤1000000000,q≤1000



输出描述:

输出q行,每行一个整数,表示答案。

   

示例1

输入

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

输出

1
1
2
1
2
2
3
各位uu们,这道题可千万不能像我一样磕了一下午才看出问题啊,这道题最大的坑是:
「除了知道根结点外,其余的结点谁是父节点谁是子节点是完全不知道」,因此要从无向图变为有向图才能确定树的层级关系
(我最开始的时候就是因为题目的样例写了个踩坑的代码结果半天没看出问题)
所以说这道题要先建立「无向无权图」,然后再通过bfs层序遍历变为「有向无权图」
然后接下来就好做啦,直接对要求的结点以及子节点做dfs,将颜色编号以及对应结果数记录下来就OK了
下面有详细代码和注解,自认为是比较好看懂的吧~
import java.util.*;
public class Main {
	public static void main(String[] args) {
		// 因为是不成环的图所以边数为节点数 - 1
		Scanner sc = new Scanner(System.in);
		int nodeNum = sc.nextInt();

		// 先使用ColorNode[]来存储所有结点并构建:无向无权不成环的图
		ColorNode[] nodes = new ColorNode[nodeNum + 1];
		for (int i = 0; i < nodeNum - 1; i++) {
			int u = sc.nextInt();
			int v = sc.nextInt();

			if (nodes[u] == null) {
				nodes[u] = new ColorNode();
			}
			if (nodes[v] == null) {
				nodes[v] = new ColorNode();
			}
			nodes[v].subTree.add(nodes[u]);
			nodes[u].subTree.add(nodes[v]);
		}

		// 无向图变为有向图
		modify(nodes[1]);

		sc.nextLine();
		// 设置颜色
		String[] split = sc.nextLine().split(" ");
		for (int i = 1; i <= nodeNum; i++) {
			nodes[i].color = Integer.valueOf(split[i - 1]);
		}

		// 记录开始的结点编号
		int loop = sc.nextInt();
		int[] res = new int[loop];
		int[] opNum = new int[loop];
		sc.nextLine();
		for (int i = 0; i < loop; i++) {
			opNum[i] = Integer.valueOf(sc.nextLine());
		}

		// 遍历要操作的结点
		for (int i = 0; i < loop; i++) {
			int opeNode = opNum[i];

			// 深搜并将结果保存在map中
			HashMap<Integer, Integer> map = new HashMap<>();
			dfs(nodes[opeNode], map);

			// 按照value降序排序
			List<Map.Entry<Integer, Integer>> list = new ArrayList<>(map.entrySet());
			Collections.sort(list, (Map.Entry<Integer, Integer> entry1, Map.Entry<Integer, Integer> entry2) -> {
				return entry2.getValue() - entry1.getValue();
			});

			// 找到符合要求的颜色
			int curRes = list.get(0).getKey();
			int curMaxNum = list.get(0).getValue();
			int j = 1;
			// 如果出现重复的最大数量颜色编号,则找到最小的那个
			while (j < list.size() && list.get(j).getValue() == curMaxNum) {
				curRes = list.get(j).getKey() < curRes ? list.get(j).getKey() : curRes;
				j++;
			}
			// 保存结点
			res[i] = curRes;
		}

		// 打印结果
		for (int i = 0; i < res.length; i++) {
			System.out.println(res[i]);
		}

	}

	// 深搜:类似前序遍历
	public static void dfs(ColorNode root, HashMap<Integer, Integer> map) {
		if (root == null)
			return;

		int color = root.color;
		Integer aDefault = map.getOrDefault(color, -1);
		if (aDefault == -1) {
			// 说明当前颜色第一次被检测到
			map.put(color, 1);
		} else {
			map.put(color, aDefault + 1);
		}
		for (ColorNode subNode : root.subTree) {
			dfs(subNode, map);
		}
	}

	// 从无向树变为有向树(层序遍历)
	public static void modify(ColorNode root) {
		ArrayDeque<ColorNode> deque = new ArrayDeque<>();
		deque.addLast(root);

		while (deque.size() != 0) {
			int size = deque.size();
			for (int i = 0; i < size; i++) {
				ColorNode first = deque.removeFirst();
				for (ColorNode curSub : first.subTree) {
					deque.addLast(curSub);
					curSub.subTree.remove(first);
				}
			}
		}

	}

	static class ColorNode {
		int color;
		ArrayList<ColorNode> subTree;

		public ColorNode() {
			this.color = -1;
			this.subTree = new ArrayList<>();
		}
	}
}




编辑于 2022-04-16 17:43:29 回复(0)
用map配合孩子结点法构造树,本题我觉得最关键的还是要搞清楚,除了已知根结点外,谁是父节点谁是子结点都是不确定的,这里卡了我很久,当时用Idea怎么看都是对的😂。
import java.util.*;
 
class TreeNode{
    int val;
    int color;
    List<TreeNode> Friend =new ArrayList<>();
    public TreeNode(int val){
        this.val=val;
    }
}
 
public class Main{
    public static void  modify(TreeNode root){
        if(root.Friend.size()==0){
            return;
        }
        for (TreeNode node : root.Friend) {
            node.Friend.remove(root);
            modify(node);
        }
    }
 
    public static void dfs(TreeNode root,Map<Integer,Integer> colormap){
        if(root==null)return;
        colormap.put(root.color, colormap.getOrDefault(root.color,0)+1);
        for (TreeNode treeNode : root.Friend) {
            dfs(treeNode,colormap);
        }
    }
    public static void main(String[] args){
        /*第一行一个正整数n表示这颗树上节点的个数。
接下来n-1行,每行两个正整数xi,yi,表示树上第i条边连接的两个节点。
接下来一行n个正整数ci,分别表示从1~n号节点上的苹果的颜色。
接下来一行一个正整数q,表示接下来有q次独立的询问。
接下来q行,每行一个正整数t表示询问:如果小团只能从节点t的子树中选取某一种颜色的苹果,选取什么颜色能拿到最多的苹果?
如果有多种颜色都可以拿同样多的苹果,输出颜色编号最小的那个对应的编号。
节点x的子树定义为所有将x当作祖先的节点,x也视为x的子树的一部分。
      */
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        Map<Integer, TreeNode> map=new HashMap<>();
        for(int i=0;i<n-1;i++){
            int x=sc.nextInt();
            int y=sc.nextInt();
            TreeNode node1=map.get(x);
            TreeNode node2=map.get(y);
            if(node1==null)node1=new TreeNode(x);
            if(node2==null)node2=new TreeNode(y);
            node1.Friend.add(node2);
            node2.Friend.add(node1);
            map.put(x,node1);
            map.put(y,node2);
        }
        modify(map.get(1));
        for(int i=1;i<=n;i++){
            int color=sc.nextInt();
            map.get(i).color=color;
        }
        int q=sc.nextInt();
        for(int i=0;i<q;i++){
            Map<Integer,Integer> colormap=new HashMap<>();
            int t=sc.nextInt();
            TreeNode root=map.get(t);
            dfs(root,colormap);
            int bestColor=0;
            int count=0;
            for (Map.Entry<Integer, Integer> entry : colormap.entrySet()) {
                if(entry.getValue()==count)bestColor=Math.min(entry.getKey(),bestColor);
                else if(entry.getValue()>count){
                    bestColor=entry.getKey();
                    count=entry.getValue();
                }
            }
            System.out.println(bestColor);
        }
    }
}


发表于 2022-03-19 16:11:39 回复(0)