###### tags: `apcs` `歷屆練習` `解題筆記` `自主學習` 樹狀圖結構題型練習 === [TOC] {%hackmd BJrTq20hE %} ## 樹狀圖結構 由於時間有限。來不及學習困難的演算法,這裡用較耗時的物件導向方式模擬樹狀圖。創一個類別儲存節點,有值、高度、父節點、一個ArrayList存放子結點 :::info 不是最佳解法,比較耗時,是比較好理解的作法 ::: ```java= class E{ //值、高度 int value,layers; //父結點 E parent; E(int value,int layers){ this.layers = layers; this.value = value; } //子結點 ArrayList<E> children = new ArrayList<>(); } ``` ## 高度計算 輸入後將葉結點獨立出來運算,每個葉結點都往上跑持續,如果父結點大於爺結點+1就不更新,最後就達成所有層數都是最大的。 ```java= for(int i = 0;i < leaf.size();i++) { int x = leaf.get(i); int now = tree.get(x).value; while(true){ if(tree.get(now).parent == null) break; //沒超過就不加 if(tree.get(now).parent.layers <tree.get(now).layers+1) { tree.get(now).parent.layers =tree.get(now).layers+1; } //跳下一個 now = tree.get(now).parent.value; } } ``` ## [APCS歷屆:樹狀圖分析]([https:/](https://zerojudge.tw/ShowProblem?problemid=c463)/) ![](https://i.imgur.com/d7bGTZW.jpg) 把高度算完後加總高度 ```java= import java.io.*; import java.util.*; public class zj_c463 { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int n = Integer.parseInt(br.readLine()); ArrayList<E> tree = new ArrayList<>(); //建立結點 for(int i = 0;i <= n;i++) { tree.add(new E(i,0)); } //建立樹的關係 for(int i = 1;i <= n;i++) { String[] d = br.readLine().split(" "); int t = Integer.parseInt(d[0]); if(t > 0) tree.get(i).layers = -1; for(int j = 1;j <= t;j++) { int c = Integer.parseInt(d[j]); tree.get(i).children.add(tree.get(c)); tree.get(c).parent = tree.get(i); } } //葉結點獨立出來 ArrayList<Integer>leaf = new ArrayList<>(); int root = 0; for(int i = 1;i <= n;i++) { if(tree.get(i).parent == null) root = i; if(tree.get(i).layers == 0) leaf.add(tree.get(i).value); } //把葉結點跑一次更新 for(int i = 0;i < leaf.size();i++) { int x = leaf.get(i); int now = tree.get(x).value; while(true){ if(tree.get(now).parent == null) break; if(tree.get(now).parent.layers <tree.get(now).layers+1) { tree.get(now).parent.layers =tree.get(now).layers+1; } //跳下一個 now = tree.get(now).parent.value; } } long ans = 0; for(int i = 1;i <= n;i++) { ans+=tree.get(i).layers; } System.out.println(root); System.out.println(ans); } } class E{ //值、高度 int value,layers; //父結點 E parent; E(int value,int layers){ this.layers = layers; this.value = value; } //子結點 ArrayList<E> children = new ArrayList<>(); } ``` ## [APCS歷屆:血緣關係](https://zerojudge.tw/ShowProblem?problemid=b967) ![](https://i.imgur.com/PQFR49Q.jpg) 第四子題有點超時 建立高度後,每個節點跑一次,一個子結點:最佳親緣關係為子結點高度+1;兩個以上:高度最高的兩個子結點高度相加再+2,找最大值 ```java= import java.io.BufferedReader; import static java.lang.Integer.*; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Arrays; public class zj_b967 { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String s = ""; while((s = br.readLine()) != null) { //輸入 int n = parseInt(s); ArrayList<E> list = new ArrayList<>(); //建立0到n-1的節點(值,層數(預設0),父節點(預設null)) for(int i = 0;i < n;i++) { list.add(new E(i,0)); } //建立關係 for(int i = 0;i < n-1;i++) { String[] d = br.readLine().split(" "); int a = parseInt(d[0]); int b = parseInt(d[1]); list.get(b).parent = list.get(a); list.get(a).children.add(list.get(b)); //將不是葉節點的節點標記 list.get(a).layers = -1; } //紀錄葉節點的編號 ArrayList<Integer> temp = new ArrayList<>(); for(int i = 0;i < n;i++) { if(list.get(i).layers == 0) { temp.add(i); } } //把每個葉節點往上跑直到頂(parent=null)刷新每個節點的層數大小 for(int i = 0;i < temp.size();i++) { int x = temp.get(i); int now = list.get(x).value; while(true) { //到頂就結束 if(list.get(now).parent == null) { break; } //刷新層數大小 if(list.get(now).parent.layers <list.get(now).layers+1) { list.get(now).parent.layers =list.get(now).layers+1; } //跳下一個 now = list.get(now).parent.value; } } int ans = 0; for(int i = 0;i < n;i++) { //如果子節點數量為1,長度為子節點層數+1 //如果子節點數量大於2,長度為最大層數的兩個子節點+2 int x = 0; int size = list.get(i).children.size(); if(size == 1) { x = list.get(i).children.get(0).layers+1; }else if(size >= 2) { int[] tmp = new int[size]; for(int j = 0;j < size;j++) { tmp[j] = list.get(i).children.get(j).layers; } Arrays.sort(tmp); x = tmp[size-1] + tmp[size-2] + 2; } //只取最大值所以超過再刷新 if(x > ans) ans = x; } System.out.println(ans); } } } //建立一個有值、層數、父節點的class,用arrayList存子節點 class E{ int value,layers; E parent; E(int value,int layers){ this.layers = layers; this.value = value; } ArrayList<E> children = new ArrayList<>(); } ``` ## APCS歷屆:貨物分配 ![](https://i.imgur.com/JIQ3sdt.jpg) 這裡用陣列儲存二元線段樹,判斷左右重量運算,可惜最後三個側資MLE沒有拿滿 ```java= import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class zj_f163 { public static int cl = 0,cr = 1,p = 2,weight = 3,n,m,hasBeenCounted = 4; //cl左子結,cr右子結,p父結,weight重量 public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); //輸入 String[] d = br.readLine().split(" "); n = Integer.parseInt(d[0]); m = Integer.parseInt(d[1]); int[] w = new int[m]; int[][] tree = new int[n*2][5]; //輸入原始重量 d = br.readLine().split(" "); for(int i = 0;i <n;i++) { tree[i+n][weight] = Integer.parseInt(d[i]); } //輸入要計算的東西 d = br.readLine().split(" "); for(int i = 0;i < m;i++) w[i] = Integer.parseInt(d[i]); //建立二元樹 for(int i = 0;i <n-1;i++) { d = br.readLine().split(" "); int a = Integer.parseInt(d[0]); int b = Integer.parseInt(d[1]); int c = Integer.parseInt(d[2]); tree[a][cl] = b; tree[a][cr] = c; tree[b][p] = a; tree[c][p] = a; } //計算節點重量 count(tree,1); //判斷 for(int i :w) { int place = 1; while(true) { //左子結重量小於或等於右子結重量:place變左 if(tree[tree[place][cl]][weight] <= tree[tree[place][cr]][weight]) place = tree[place][cl]; //右小於左,place變右 else place = tree[place][cr]; //結點重量加入 tree[place][weight] += i; //place超過分裝站範圍退出 if(place>= n) break; } System.out.print(place+" "); } } //計算結點重量 public static void count(int[][] t,int x) { if(x>= n) return; count(t,t[x][cl]); count(t,t[x][cr]); t[x][weight] = t[t[x][cl]][weight]+t[t[x][cr]][weight]; } } ```