版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/kzn2683331518/article/details/83212485
问题描述:
给定一个序列,判断其是否可能为一个二叉树的中序遍历序列,该二叉树树边连接的两个点的值不能互质。
输入描述:
第一行一个数字T,表示测试组数
对于每一组测试样例
第一行一个数字n,表示序列长度
第二行有n个数字ai,表示这个序列
T≤5,n≤500,2≤ai≤10^9
输出格式:
输出T行,"Yes"或"No"
输入样例:
2
6
5 4 7 9 5 4
4
2 6 3 4
输出样例:
No
Yes
题目分析:
n才500,预处理出 ok[i][j] 表示a[i]与a[j]之间可以连树边。接下来考虑区间 dp,L[l][r]表示区间[l,r] 是否可以作为 r 的左儿子,R[l][r]表示区间[l,r]是否可以作为 l 的右儿子。
那么 L[l][r]=true 的前提是存在一个k(l≤k≤r-1)满足: L[l][k]&&R[k][r-1]&&ok[k][r]
同理 R[l][r]=true 的前提是存在一个k(l+1≤k≤r)满足:L[l+1][k]&&R[k][r]&&ok[l][k]
分析之后发现区间dp复杂度为n^3,n为500,样例好像比较水,1s时间能过。
AC代码:
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f3f3f3f3f
#define lowbit(a) ((a)&(-(a)))
#define ll long long
using namespace std;
int n,m,a[505];
bool L[505][505],R[505][505],ok[505][505];
inline void init(){
for(int i=1;i<=n;i++){
L[i][i]=R[i][i]=ok[i][i]=1;
for(int j=1;j<i;j++){
L[i][j]=L[j][i]=R[i][j]=R[j][i]=ok[i][j]=ok[j][i]=0;
}
}
}
int main(){
int t; scanf("%d",&t);
while(t--){
scanf("%d",&n);
init();
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++){ //预处理
for(int j=1;j<i;j++){
if(__gcd(a[i],a[j])!=1)ok[i][j]=ok[j][i]=true;
else ok[i][j]=ok[j][i]=false;
}
}
for(int len=1;len<=n;len++){
for(int i=len+1;i<=n;i++) //L[i-len][i]
for(int j=i-len;j<=i-1;j++)
if(L[i-len][j]&&R[j][i-1]&&ok[i][j]){
L[i-len][i]=true;
break;
}
for(int i=1;i<=n-len;i++) //R[i][i+len]
for(int j=i+1;j<=i+len;j++)
if(L[i+1][j]&&R[j][i+len]&&ok[i][j]){
R[i][i+len]=true;
break;
}
}
bool flag=false;
for(int i=1;i<=n;i++)
if(L[1][i]&&R[i][n]){
flag=true;
break;
}
if(flag)
puts("Yes");
else
puts("No");
}
return 0;
}