2018GDUT第一場C 遺失的二叉樹(區間dp)
阿新 • • 發佈:2018-12-16
問題描述:
給定一個序列,判斷其是否可能為一個二叉樹的中序遍歷序列,該二叉樹樹邊連線的兩個點的值不能互質。
輸入描述:
第一行一個數字T,表示測試組數
對於每一組測試樣例
第一行一個數字n,表示序列長度
第二行有n個數字ai,表示這個序列
T≤5,n≤500,2≤ai≤10^9
輸出格式:
輸出T行,"Yes"或"No"
輸入樣例:
2
6
5 4 7 9 5 4
4
2 6 3 4
輸出樣例:
No
Yes
題目分析:
n才500,預處理出 ok[i][j] 表示a[i]與a[j]之間可以連樹邊。接下來考慮區間 dp,L[l][r]表示區間[l,r] 是否可以作為 r 的左兒子,R[l][r]表示區間[l,r]是否可以作為 l 的右兒子。
那麼 L[l][r]=true 的前提是存在一個k(l≤k≤r-1)滿足: L[l][k]&&R[k][r-1]&&ok[k][r]
同理 R[l][r]=true 的前提是存在一個k(l+1≤k≤r)滿足:L[l+1][k]&&R[k][r]&&ok[l][k]
分析之後發現區間dp複雜度為n^3,n為500,樣例好像比較水,1s時間能過。
AC程式碼:
#include<bits/stdc++.h> #define INF 0x3f3f3f3f3f3f3f3f #define lowbit(a) ((a)&(-(a))) #define ll long long using namespace std; int n,m,a[505]; bool L[505][505],R[505][505],ok[505][505]; inline void init(){ for(int i=1;i<=n;i++){ L[i][i]=R[i][i]=ok[i][i]=1; for(int j=1;j<i;j++){ L[i][j]=L[j][i]=R[i][j]=R[j][i]=ok[i][j]=ok[j][i]=0; } } } int main(){ int t; scanf("%d",&t); while(t--){ scanf("%d",&n); init(); for(int i=1;i<=n;i++)scanf("%d",&a[i]); for(int i=1;i<=n;i++){ //預處理 for(int j=1;j<i;j++){ if(__gcd(a[i],a[j])!=1)ok[i][j]=ok[j][i]=true; else ok[i][j]=ok[j][i]=false; } } for(int len=1;len<=n;len++){ for(int i=len+1;i<=n;i++) //L[i-len][i] for(int j=i-len;j<=i-1;j++) if(L[i-len][j]&&R[j][i-1]&&ok[i][j]){ L[i-len][i]=true; break; } for(int i=1;i<=n-len;i++) //R[i][i+len] for(int j=i+1;j<=i+len;j++) if(L[i+1][j]&&R[j][i+len]&&ok[i][j]){ R[i][i+len]=true; break; } } bool flag=false; for(int i=1;i<=n;i++) if(L[1][i]&&R[i][n]){ flag=true; break; } if(flag) puts("Yes"); else puts("No"); } return 0; }