區間dp:數字二叉搜索樹問題

題面:

給定一些數,講這些數拼成一顆樹,要求這棵樹是一顆二叉搜索樹,同時任意樹邊相連的兩個節點的gcd(greatest common divisor)都超過1。

(GCD:最大公約數,兩個或多個整數共有約數中最大的一個 ,例如8和6的最大公約數是2)

輸入第一行一個t,表示數據組數。
對於每組數據,第一行輸入一個n,表示數的個數
接下來一行有n個數aia_i,輸入保證是升序的

每組數據輸出一行,如果能夠造出來滿足題目描述的樹,輸出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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章