题目描述
给出n个数,求能否构造出一颗二叉搜索树,同时满足任意树边所连两个数的gcd都超过1
Input
第一行一个数t,代表数据组数
对于每组数据,第一行一个数n,保证升序
接下来一行n个数
Output
每组数据输出一行,如果能够造出来满足题目描述的树,输出Yes,否则输出No。
解题思路
看似一道树的题,其实是一个区间dp的问题,用e[i][j]代表i和j是否能够连接(gcd均>1),且由于数据是有序的,则对于第k个数,可以用L[i][k]表示[i,k]的数是否能作为k的左子树,用R[k][j]表示[k,j]的数是否能作为k的右子树,然后再根据e[][]的值来更新L和R,从小区间逐步判断到大区间,直至整个[1,n]区间。
实现代码
#include<iostream>
using namespace std;
int a[705];
int n;
bool e[705][705],L[705][705],R[705][705];
int gcd(int a,int b)
{return b==0?a:gcd(b,a%b);}
bool judge()
{
for(int l=n-1;l>=0;l--)
{
for(int r=l;r<n;r++)
{
for(int k=l;k<=r;k++)
{
if(L[l][k]&&R[k][r])
{
if(l==0&&r==n-1)
return true;
if(e[l-1][k])
R[l-1][r]=1;
if(e[r+1][k])
L[l][r+1]=1;
}
}
}
}
return false;
}
void init()
{
for(int i=0;i<705;++i)
{
for(int j=0;j<705;++j)
{
e[i][j]=false;
L[i][j]=false;
R[i][j]=false;
}
}
}
int main()
{
int T;
cin>>T;
while(T--)
{
cin>>n;
init();
for(int i=0;i<n;++i)
{
cin>>a[i];
L[i][i]=true;
R[i][i]=true;
}
for(int i=0;i<n;++i)
{
for(int j=i;j<n;++j)
{
if(gcd(a[i],a[j])>1)
e[i][j]=e[j][i]=true;
else
e[i][j]=e[j][i]=false;
}
}
if(judge())
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
}
return 0;
}
总结
这道题一上来是确实没看懂,一直以为是构建树来判断,感觉无从下手,后来与同学交流后才知道,这原来是一道dp的题目,感觉对dp的了解还是不够,简单的直接的dp题目,还能看明白一点,这种属实没有好的解决办法,还是需要加强练习。