題目鏈接:http://codeforces.com/contest/1025/problem/D
題目大意:已知一棵二叉搜索樹的n個結點的權值,同時知道只有在兩個權值gcd不爲1的點之間纔會有邊。現在給出這n個結點的權值(按升序給出),問你這n個結點是否能組成一棵二叉搜索樹。
題目思路:一開始沒往dp方面想,就傻傻地想着用特判的方法去處理一些特殊情況,寫到後面才發現情況太多了根本無法處理。後面看了大神的博客才懂了這題得用區間dp來寫。
我們設表示第 i 個結點到第 j 個結點作爲某一棵二叉搜索樹的左子樹是否可行,表示作爲右子樹是否可行。
接下來就可以進行狀態轉移,我們分別枚舉二叉搜索樹的大小,起點和終點,以及根節點的位置。
轉移完之後再枚舉最後的根節點是哪個即可。
具體實現看代碼:
#include <bits/stdc++.h>
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pb push_back
#define MP make_pair
#define lowbit(x) x&-x
#define clr(a) memset(a,0,sizeof(a))
#define _INF(a) memset(a,0x3f,sizeof(a))
#define FIN freopen("in.txt","r",stdin)
#define IOS ios::sync_with_stdio(false)
#define fuck(x) cout<<"["<<#x<<" "<<(x)<<"]"<<endl
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll>pll;
typedef pair<int, int>pii;
typedef vector<int> VI;
const int inf = 0x3f3f3f3f;
const int MX = 700 + 7;
int n;
int p[MX];
bool dp[MX][MX][2], g[MX][MX], flag;
ll gcd(ll a, ll b) {
return b == 0 ? a : gcd(b, a % b);
}
int main() {
//FIN;
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &p[i]);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (i == j) continue;
g[i][j] = gcd(p[i], p[j]) > 1;
}
}
for (int l = 1; l <= n; l++) {
for (int i = 1, j; i + l - 1 <= n; i++) {
j = i + l - 1;
for (int k = i; k <= j; k++) {
flag = 1;
if (k == i) flag &= 1;
else flag &= dp[i][k - 1][0];
if (k == j) flag &= 1;
else flag &= dp[k + 1][j][1];
if (flag) {
dp[i][j][0] |= g[k][j + 1];
dp[i][j][1] |= g[k][i - 1];
}
}
}
}
for (int i = 1; i <= n; i++) {
flag = 1;
if (i == 1) flag &= 1;
else flag &= dp[1][i - 1][0];
if (i == n) flag &= 1;
else flag &= dp[i + 1][n][1];
if (flag) {
puts("Yes");
return 0;
}
}
puts("No");
return 0;
}