問題描述
在瑞神大戰宇宙射線中我們瞭解到了宇宙狗的厲害之處,雖然宇宙狗凶神惡煞,但是宇宙狗有一個很可愛的女朋友。
最近,他的女朋友得到了一些數,同時,她還很喜歡樹,所以她打算把得到的數拼成一顆樹。
這一天,她快拼完了,同時她和好友相約假期出去玩。貪喫的宇宙狗不小心把樹的樹枝都喫掉了。所以恐懼包圍了宇宙狗,他現在要恢復整棵樹,但是它只知道這棵樹是一顆二叉搜索樹,同時任意樹邊相連的兩個節點的gcd(greatest common divisor)都超過1。
但是宇宙狗只會發射宇宙射線,他來請求你的幫助,問你能否幫他解決這個問題。
補充知識:
GCD:最大公約數,兩個或多個整數共有約數中最大的一個 ,例如8和6的最大公約數是2。
一個簡短的用輾轉相除法求gcd的例子:
int gcd(int a,int b){return b == 0 ? a : gcd(b,a%b);}
Input
輸入第一行一個t,表示數據組數。
對於每組數據,第一行輸入一個n,表示數的個數
接下來一行有n個數,輸入保證是升序的。
Output
每組數據輸出一行,如果能夠造出來滿足題目描述的樹,輸出Yes,否則輸出No。
無行末空格。
Sample input & output
Sample input1
1
6
3 6 9 18 36 108
Sample output1
Yes
Sample input2
2
2
7 17
9
4 8 10 12 15 18 33 44 81
Sample output2
No
Yes
數據範圍
提示
第一個樣例可以構建下圖所示的二叉搜索樹
解題思路
這道題開始沒想到用區間dp來做,想了一個自己認爲“絕妙的方法”。利用棧來維護一條主鏈,方法就不說了,畢竟結果炸了。這個方法挺“絕命的”,樣例全過,直接0分。
二叉搜索樹具有明顯的子結構特性,這個題雖然要構成一個二叉樹,但是給出的是一個區間,因此這個題還是要用區間dp來做的,樹的結構不能丟,我們定義兩個數組:分別表示以爲根,向左到可以作爲的左子樹,向右到可以作爲的右子樹。樹結構非常重要,雖然是區間dp,但是這個結果要構成一個二叉搜索樹,因此必須有這兩個數組。
然後用表示能構成一棵樹。函數最後返回。(我以前嘗試過直接表示能構成一棵樹,然後拼接用拼接,這樣錯的原因是,能夠構成一棵樹,不一定表示左子樹,能夠構成一棵樹,不一定表示右子樹,那麼就不一定能拼接起來。2,3,5,30這樣四個節點數據就可以卡死。)
當時,,此時是以爲根的二叉搜索樹。
狀態轉移是每次轉移一個點,在的情況下:
轉移方程爲什麼這麼寫呢,先看第一個方程,首先這個時候我們已經保證了從到這段區間可以構成一棵二叉搜索樹,並且根節點是,注意這個根節點很重要。如果說了,那麼二者之間就可以連邊,並且注意數組的定義:表示以爲根,向左到可以作爲的左子樹,那麼的左兒子就是了。在枚舉的過程中,只要有一次可以連邊,那麼。所以中間使用的是符號。第二個方程同理。
完整代碼
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
using namespace std;
const int maxn=700+100;
int t,n,a[maxn];
bool dp[maxn][maxn],g[maxn][maxn],l[maxn][maxn],r[maxn][maxn];
int gcd(int _a,int _b) {return _b==0? _a: gcd(_b,_a%_b);}
void init(){
memset(l,false,sizeof(l));
memset(r,false,sizeof(r));
memset(dp,false,sizeof(dp));
for (int i=1; i<=n; i++) {
l[i][i]=r[i][i]=dp[i][i]=true;
for (int j=1; j<=n; j++)
g[i][j]=gcd(a[i],a[j])>1;
}
}
bool IsBinarySearchTree(){
for (int len=1; len<=n; len++){
for (int i=1; i<=n-len+1; i++){
int j=i+len-1;
for (int k=i; k<=j; k++){
if(l[k][i] && r[k][j]){
dp[i][j]=true;
l[j+1][i]|=g[j+1][k];
r[i-1][j]|=g[i-1][k];
}
}
}
}
return dp[1][n];
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for (int i=1; i<=n; i++) scanf("%d",&a[i]);
init();
if(IsBinarySearchTree()) printf("Yes\n");
else printf("No\n");
}
return 0;
}