问题描述:
在瑞神大战宇宙射线中我们了解到了宇宙狗的厉害之处,虽然宇宙狗凶神恶煞,但是宇宙狗有一
个很可爱的女朋友。
最近,他的女朋友得到了一些数,同时,她还很喜欢树,所以她打算把得到的数拼成一颗树。
这一天,她快拼完了,同时她和好友相约假期出去玩。贪吃的宇宙狗不小心把树的树枝都吃掉
了。所以恐惧包围了宇宙狗,他现在要恢复整棵树,但是它只知道这棵树是一颗二叉搜索树,同
时任意树边相连的两个节点的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个数
a
i
a_i
ai,输入保证是升序的。
Output:
每组数据输出一行,如果能够造出来满足题目描述的树,输出Yes,否则输出No。
无行末空格。
Sample Input1:
1 16
3 6 9 18 36 108
Sample Output1:
Yes
Sample Input2:
2 22
7 17
9
4 8 10 12 15 18 33 44 81
Sample Output2:
No
Yes
样例解释
样例1可构造如下图:
解题思路:
本题是动态规划题,可以知道,二叉搜索树的特征是:碰到一棵树的根,就能知道要去左子树还是右子树寻找元素。对于该题,输入升序,可以知道,对于一个根root=a[i],可以知道,j<i,a[j]都在它的左子树内,k>i,a[k]都在它的右子树内,要构造这样一棵gcd二叉搜索树,必须要所有子树都满足条件,即子树也是gcd二叉搜索树。故可以用到dp的思想。
fl[i][zi]表示以a[i]为根的右子树数目为zi的树是否符合条件(fl即左父亲的意思),同理,fr[i][zi]表示以a[i]为根,左子树数目为zi的树是否符合条件。fl[j][zi]符合条件的要求:存在i,fr[i][i-j-1]&&fl[i][j+zi-i]&&connect(a[i],a[j]),fr[j][zi]符合条件的要求:存在i,fr[i][i-j+zi]&&fl[i][j-i-1]&&connect(a[i],a[j])。当存在某点root满足条件:fl[root][root]&&fr[root][n-root-1],即覆盖区间,构造完成,输出Yes即可,若所有循环结束后没有这样的root,则不存在,输出No。
实验代码:
#include<iostream>
#include<memory.h>
#include<math.h>
using namespace std;
int a[710],n;
bool fl[710][710],fr[710][710];
int gcd(int a,int b){return b == 0 ? a : gcd(b,a%b);}
bool connect(int a,int b)
{
if(gcd(a,b)>1)
return true;
return false;
}
void handle(int zi)
{
int l=zi;
int r=n-zi-1;
for(int j=0;j<=r;j++)
{
for(int i=j+1;i<=j+zi;i++)
{
if(fr[i][i-j-1]&&fl[i][j+zi-i]&&connect(a[i],a[j]))
{
fl[j][zi]=true;
break;
}
}
}
for(int j=l;j<n;j++)
{
for(int i=j-zi;i<j;i++)
{
if(fr[i][i-j+zi]&&fl[i][j-i-1]&&connect(a[i],a[j]))
{
fr[j][zi]=true;
break;
}
}
}
}
bool getAns()
{
memset(fl,0,sizeof(fl));
memset(fr,0,sizeof(fr));
for(int i=0;i<n;i++)
fl[i][0]=1;
for(int i=0;i<n;i++)
fr[i][0]=1;
int up=ceil((n-1)/(float)2);
int zi=1,l,r;
for(;zi<up;zi++)
handle(zi);
for(;zi<n;zi++)
{
handle(zi);
l=zi;
r=n-zi-1;
for(int i=0;i<=r;i++)
if(fl[i][zi]&&fr[i][n-zi-1])
return true;
for(int i=l;i<n;i++)
if(fr[i][zi]&&fl[i][n-zi-1])
return true;
}
return false;
}
int main(void)
{
int T;
cin>>T;
while(T--)
{
cin>>n;
for(int i=0;i<n;i++)
cin>>a[i];
if(getAns())
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
}
return 0;
}