Codeforces Round #505 D. Recovering BST(区间dp)

题目链接:http://codeforces.com/contest/1025/problem/D

题目大意:已知一棵二叉搜索树的n个结点的权值,同时知道只有在两个权值gcd不为1的点之间才会有边。现在给出这n个结点的权值(按升序给出),问你这n个结点是否能组成一棵二叉搜索树。

题目思路:一开始没往dp方面想,就傻傻地想着用特判的方法去处理一些特殊情况,写到后面才发现情况太多了根本无法处理。后面看了大神的博客才懂了这题得用区间dp来写。

我们设dp[i][j][0]表示第 i 个结点到第 j 个结点作为某一棵二叉搜索树的左子树是否可行,dp[i][j][1]表示作为右子树是否可行。

接下来就可以进行状态转移,我们分别枚举二叉搜索树的大小,起点和终点,以及根节点的位置。

转移完之后再枚举最后的根节点是哪个即可。

具体实现看代码:

#include 
#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)<<"]"<pll;
typedef pairpii;
typedef vector 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;
}

 

你可能感兴趣的:(dp,ACM)