宇宙狗的危机
Examples
题目分析
- 总体上采用区间dp的思想,在计算过程中发现需要附加信息以完成计算,在搜索根节点的信息很重要中所以额外存储了根节点的信息
- 定义dp[i][j]表示i-j是否能形成一颗满足要求的树,l[i][j]表示i-j形成的子树种i节点可以作为根节点,r[i][j]表示i-j形成的子树j节点可以作为根节点
- 状态转移方程
if dp[i+1][j]:
for k from i to j-1:
如果能i+1-k能形成一棵以k为根的树,k-j可以形成一棵以k为根的子树树且gcd(a[i],a[k])>1:
dp[i][j] = 1
l[i][j] = 1
break;
if dp[i][j-1]:
for k from i to j-1:
如果能i-k能形成一棵以k为根的树,k-j-1可以形成一棵以k为根的子树树且gcd(a[j],a[k])>1:
dp[i][j] = 1
r[i][j] = 1
break;
for k from i+1 to j-1:
如果能i-k能形成一棵以k为根的树,k-j可以形成一棵以k为根的子树树:
dp[i][j] = 1
break;
完整代码
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
const int maxn = 800;
int t, n;
int a[maxn];
int dp[maxn][maxn], l[maxn][maxn], r[maxn][maxn];
int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
int main()
{
cin >> t;
while (t--)
{
cin >> n;
for (int i = 0; i < n; i++)
cin >> a[i];
for (int i = 0; i < n; i++)
{
dp[i][i] = 1;
r[i][i] = 1;
l[i][i] = 1;
}
for (int len = 2; len <= n; len++) {
for (int i = 0; i <= n - len; i++)
{
int j = i + len - 1;
if (dp[i + 1][j]) {
for (int k = i + 1; k <= j; k++)
{
if (r[i + 1][k] && l[k][j] && (gcd(a[i], a[k]) > 1))
{
dp[i][j] = 1;
l[i][j] = 1;
break;
}
}
}
if (dp[i][j - 1]) {
for (int k = i; k < j; k++)
{
if (r[i][k] && l[k][j - 1] && (gcd(a[j], a[k]) > 1))
{
dp[i][j] = 1;
r[i][j] = 1;
break;
}
}
}
for (int k = i + 1; k < j; k++)
{
dp[i][j] = max(dp[i][j], dp[i][k] & dp[k][j] & r[i][k] & l[k][j]);
if (dp[i][j])
break;
}
}
}
if (dp[0][n - 1])
cout << "Yes" << endl;
else
cout << "No" << endl;
memset(dp, 0, sizeof(dp));
memset(r, 0, sizeof(r));
memset(l, 0, sizeof(l));
}
return 0;
}
note
感觉这个题对我来说不好想,花了很长时间,最开始的时候意识到信息不够时总想通过增加维数来增加信息,现在意识到可以单独开辟一块位置来存储信息。另外,如何保留有用的信息也是一个难点。