TT数鸭子&ZJM要抵御宇宙射线
这两道题目比较简单,只需要注意如下几点:
(1)鸭子映射成的数字数位中不同的数字个数最大为10,即这个数字中出现了0、1、2、3、4、5、6、7、8、9。但是题目描述中,给出的k值范围很令人迷惑,所以要相信自己的理解,坚定自己的想法。
(2)给出的时间是5s,而且最多只有1000个点,说明可以暴力枚举。计算出以每个点为圆心算出来的半径,之后选最小值。
宇宙狗的危机
问题描述
思路分析
小建议:涉及动态规划的题目都比较难,根据时间把握情况,尝试暴力求解前几个点,可能会得一些分数。暴力求解:对每个节点进行枚举,检查以该点为根是否可以构成二叉搜索树。
动态规划思路:首先定义二维数组f1,f2,le,ri,f1[i][j]=1表示第i个节点到第j个节点可以构成二叉搜索树,f2[i][j]=1表示第i个和第j个节点的公约数大于1,le[i][root]=1表示以root为根,向左到第i个节点作为root的左子树,形成合法的二叉搜索树。re[root][j]表示以root为根,向右到第j个节点构成其右子树,形成合法的二叉搜索树。
状态转移方程:
if(le[j][root]&&ri[root][k])
{
f1[j][k]=1;//标记F1
if(f2[j-1][root]) ri[j-1][k]=1;
if(f2[root][k+1]) le[j][k+1]=1;
}
当le[j][root]&&ri[root][k]为真时,说明保证了从j到k能构成根为root的二叉搜索树,则令f1[j][jk]=1。如果f2[j-1][root]=1,说明第j-1个节点可以连接到以root为根的二叉搜索树的左子树中,那么说明第j-1到第k个节点可以构成root的左子树;f2[root][k+1]=1分析同理。
代码实现
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=750;
int T,n,a[maxn];
int f1[maxn][maxn],f2[maxn][maxn],le[maxn][maxn],ri[maxn][maxn];
//f1[i][j]i到j是否能构造二叉搜索树
//le[i][j]表示以j为根,向左到i可以作为i的左子树
//ri[i][j]表示以i为根,向右到j作为i的右子树
int gcd(int x,int y){
return y==0?x:gcd(y,x%y);
}
int main(){
scanf("%d",&T);
while(T--){
memset(f1,0,sizeof(f1));memset(f2,0,sizeof(f2));
memset(le,0,sizeof(le));memset(ri,0,sizeof(ri));
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
le[i][i]=1;ri[i][i]=1;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(gcd(a[i],a[j])>1&&i!=j) f2[i][j]=1;
//记录任意树边满足公约数超过1的
}
}
for(int i=0;i<n;i++){
for(int j=1;i+j<=n;j++){
int k=i+j;
for(int root=j;root<=k;root++){
if(le[j][root]&&ri[root][k]){//如果左子树和右子树都是合法的二叉搜索树
f1[j][k]=1;
if(f2[j-1][root]) ri[j-1][k]=1;
if(f2[root][k+1]) le[j][k+1]=1;
}
}
}
}
if(f1[1][n]) printf("Yes\n");
else printf("No\n");
}
return 0;
}