题意描述
在瑞神大战宇宙射线中我们了解到了宇宙狗的厉害之处,虽然宇宙狗凶神恶煞,但是宇宙狗有一个很可爱的女朋友。
最近,他的女朋友得到了一些数,同时,她还很喜欢树,所以她打算把得到的数拼成一颗树。
这一天,她快拼完了,同时她和好友相约假期出去玩。贪吃的宇宙狗不小心把树的树枝都吃掉了。所以恐惧包围了宇宙狗,他现在要恢复整棵树,但是它只知道这棵树是一颗二叉搜索树,同时任意树边相连的两个节点的gcd(greatest common divisor)都超过1。
但是宇宙狗只会发射宇宙射线,他来请求你的帮助,问你能否帮他解决这个问题。
补充知识:
GCD:最大公约数,两个或多个整数共有约数中最大的一个 ,例如8和6的最大公约数是2。
一个简短的用辗转相除法求gcd的例子:
int gcd(int a,int b){return b == 0 ? a : gcd(b,a%b);}
输入
输入第一行一个t,表示数据组数。
对于每组数据,第一行输入一个n,表示数的个数
接下来一行有n个数,输入保证是升序的。
输出
每组数据输出一行,如果能够造出来满足题目描述的树,输出Yes,否则输出No。
无行末空格。
输入样例 1
1
6
3 6 9 18 36 108
1
2
3
输出样例 1
Yes
1
输入样例 2
2
2
7 17
9
4 8 10 12 15 18 33 44 81
1
2
3
4
5
输出样例2
No
Yes
思路:
dp构建可行二叉搜索树!
所有的数据都是有序的,可以考虑区间dp。to_l[i][j]代表[i,j-1]是否可作为j的左子树。to_r[i][j]表示[i+1,j]是否可以作为i的右子树。
转移条件为:if (GCD[l-1][root]>1)R[l-1][r]=1; if (GCD[r+1][root]>1)L[l][r+1]=1;
当[l,r]为[1,n]时说明可以构建符合题意的树,如果枚举结束都到不了[1,n]说明不可以构建符合题意的树。GCD数组记录两点gcd.
初始化时注意将 to_l[i][i]和to_r[i][i]都赋值为1,因为当点i的左子树或者右子树为空时一定是符合题意的
代码:
1 #include<iostream> 2 #include<cstring> 3 using namespace std; 4 const int maxn=770; 5 int n,t; 6 int a[maxn],GCD[maxn][maxn] ; 7 bool to_l[maxn][maxn],to_r[maxn][maxn]; 8 9 int gcd(int a,int b){ 10 return b == 0 ? a : gcd(b,a%b); 11 } 12 bool solve() 13 { 14 for(int len=1;len<=n;len++) 15 { 16 for(int l=1;l<=n-len+1;l++) 17 { 18 int r=l+len-1; 19 for(int k=l;k<=r;k++) 20 { 21 if(to_l[k][l] && to_r[k][r]) 22 { 23 if(len==n) 24 { 25 return true; 26 } 27 if(GCD[l-1][k] > 1) 28 to_r[l-1][r]=1; 29 if(GCD[k][r+1] > 1) 30 to_l[r+1][l]=1; 31 } 32 } 33 } 34 } 35 return false; 36 } 37 void init(){ 38 memset(to_l,0,sizeof(to_l)); 39 memset(to_r,0,sizeof(to_r)); 40 for(int i=1;i<=n;i++) 41 { 42 for(int j=i;j<=n;j++) 43 { 44 45 GCD[j][i]=gcd(a[i],a[j]); 46 GCD[i][j]=GCD[j][i]; 47 } 48 to_l[i][i]=1; 49 to_r[i][i]=1; 50 } 51 } 52 int main() 53 { 54 cin>>t; 55 while(t--) 56 { 57 //input 58 cin>>n; 59 for(int i=1;i<=n;i++) 60 cin>>a[i]; 61 //initialize 62 init(); 63 //solve 64 bool flag = solve(); 65 if(flag){ 66 cout<<"Yes"<<endl; 67 } 68 else cout<<"No"<<endl; 69 } 70 return 0; 71 }