算法分析:本來打算使用標準的二叉樹模板自上而下遞歸檢索是否是最小堆,然而題目給的輸入結構竟然給了更好解法的提示:使用雙親表示法自下而上檢索——是的沒錯,就是套用“並查集模板”。
(複雜度nlog(n),還可以繼續優化(剪枝),不過優化完複雜度還是這個數量級,反正n很小不會超時,懶得寫了)
Problem B. 最小堆
題目描述
給定一棵帶權二叉樹,請判斷它是不是一個最小堆。
一棵二叉樹是一個最小堆,當且僅當對於樹上任意一個節點,它的權值都小於或等於以它爲根的子樹中的所有權值。
輸入格式
輸入數據第一行是一個整數T(1<=T<=100),表示測試數據的組數。
對於每組測試數據:
第一行是一個整數N(1<=N<=100),表示樹的節點個數。
接下來一行包含N個正整數,第i個整數valuei(1<=valuei<=1000)表示編號i的點的權值。
接下來N-1行,每行兩個整數u和v(1<=u,v<=N, u!=v),表示節點u是節點v的父節點。
測試數據保證給定的一定是一棵二叉樹,並且節點1是樹的根結點。
輸出格式
對於每組測試數據,如果給定的樹是一個最小堆則輸出Yes,否則輸出No。
輸入樣例
3
1
10
3
10 5 3
1 2
1 3
5
1 2 3 4 5
1 3
1 2
2 4
2 5
輸出樣例
Yes
No
Yes
/*並查集。
father[]原本記錄i結點的雙親結點下標(本質即爲樹的雙親表示法)
將findRoot方法添加了reduce deep功能後,father[]退化爲記錄i結點的根節點
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#define MAXSIZE 200
using namespace std;
bool isSmallHeap;
struct Set{ //UnionFindSet 從0開始
int father[MAXSIZE];
int value[MAXSIZE];
int setSize;
int initSet(int setSize){
this->setSize=setSize;
for (int i=0;i<MAXSIZE;i++){
father[i]=-1;
}
}
//findRoot方法功能:找到x結點的根
int findRoot(int x){
while (father[x]!=-1){
if (value[x]<value[father[x]]){
isSmallHeap=false;
return -1;//中途return
}
x=father[x];
}
return x;
}
};
int main(){
int t,n;
int u,v;
Set set;
int value;
scanf("%d",&t);
for (int time=0;time<t;time++){
scanf("%d",&n);
//initiate
set.initSet(n);
isSmallHeap=true;
//input
for (int i=0;i<n;i++){
scanf("%d",&value);
set.value[i]=value;
}
for (int i=0;i<n-1;i++){
scanf("%d%d",&u,&v);
u--;v--;
set.father[v]=u;
}
//search
for (int i=0;i<n;i++){
set.findRoot(i);
}
//output
printf("%s\n",isSmallHeap?"Yes":"No");/*bug*/
}
return true;
}