題面:
給定一些數,講這些數拼成一顆樹,要求這棵樹是一顆二叉搜索樹,同時任意樹邊相連的兩個節點的gcd(greatest common divisor)都超過1。
(GCD:最大公約數,兩個或多個整數共有約數中最大的一個 ,例如8和6的最大公約數是2)
輸入第一行一個t,表示數據組數。
對於每組數據,第一行輸入一個n,表示數的個數
接下來一行有n個數,輸入保證是升序的
每組數據輸出一行,如果能夠造出來滿足題目描述的樹,輸出Yes,否則輸出No。
sample input:
2
2
7 17
9
4 8 10 12 15 18 33 44 81
sample output:
No
Yes
sample input:
1
6
3 6 9 18 36 108
sample output:
Yes
數據組成
思路:
- 符合最優子結構所以可以使用dp來做
- 但是它和普通的dp也有區別,他的dp[i][j]不能通過dp[i-1][j],dp[j][k]這種來得到,需要對於每一個子樹的左右子樹進行判斷,判斷是否他的左右子樹滿足條件
- 所以使用bool型數組l和r來進行左右子樹的條件判斷,爲true表示從i點到j點作爲一顆二叉搜索樹的左/右子樹可行
- 進行子樹的遍歷,枚舉區間長度,枚舉區間起點終點與區間中的根節點,最後判斷整棵樹是否符合題目描述
#include<iostream>
#include<cstdio>
#include<string.h>
using namespace std;
bool l[1001][1001];
bool r[1001][1001];
bool dp[1001][1001];
bool flag[1001][1001];
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));
memset(flag,false,sizeof(flag));
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
init();
int n,a[1111];
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
l[i][i]=1;
r[i][i]=1;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i==j) continue;
else if(gcd(a[i],a[j])>=2)
flag[i][j]=1;
}
}
for(int i=0;i<n;i++)
{
for(int j=1;j+i<=n;j++)
{
for(int t=j;t<=i+j;t++)
{
if(l[j][t]&&r[t][i+j])
{
dp[j][i+j]=true;
if(flag[j-1][t]) r[j-1][i+j]=1;
if(flag[t][i+j+1]) l[j][i+j+1]=1;
}
}
}
}
if(dp[1][n]) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
return 0;
}