C-宇宙狗的危机

C-宇宙狗的危机

一、题目描述

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

二、思路与算法

本题在代码中的注释有详细说明。
核心算法就是通过递归,判断目前这个节点是否可以作为根节点。

  • 那么,如何递归确定?
    答:要想让某节点可以作为二叉搜索树的根节点,那么这个节点的左子树、右子树都必须可行。那么,我们又要去分别看左、右子树。两者判断方法相似,以左子树判断为例:
if(!vis_l[l+1][now+1]){   //左侧没有计算过 
		bool flag=false;
		if(l==now-1){flag=true;}   //左子树为空 一定可行 
		for(int i=l+1;i<now;i++){
			if(all_gcd[i][now]>1){   //gcd>1
				if(jud(l,i,now)){flag=true;}   
				//递归判断左子树能否以当前节点为根,l~i为它的左子树,i~now是它的右子树
				//以此类推直到左子树每个点的位置都确定下来 
			}
		}   //能找到一个节点,作为now的左孩子,且左子树可行 
		vis_l[l+1][now+1]=true;
		result_l[l+1][now+1]=flag;
	}

vis的两个数组用来记录是否计算过这个区间的可行性,如果没有计算过可行性,就进行上方代码。

三、代码实现

#include<iostream>
#include<math.h>
#include<algorithm>
#include<string.h>
using namespace std;

int a[710];
int all_gcd[710][710];
bool vis_l[710][710];     //是否已经计算过这部分的可行性 
bool vis_r[710][710];  
bool result_l[710][710];    //记录这部分的可行性 
bool result_r[710][710]; 

int n,t;

int gcd(int a,int b){ return b==0?a:gcd(b,a%b); }   //求gcd 

bool jud(int l,int now,int r){   //判断以now为根节点构造二叉搜索树是否可行 
	if(vis_l[l+1][now+1]&&vis_r[now+1][r+1]){   //如果左右都计算过 
		return (result_l[l+1][now+1]&&result_r[now+1][r+1]);   
		//直接返回两者可行性的与(两者都可,now才能做根) 
	}
	if(l>=now-1&&r<=now+1){
		return true;
	}
	if(!vis_l[l+1][now+1]){   //左侧没有计算过 
		bool flag=false;
		if(l==now-1){flag=true;}   //左子树为空 一定可行 
		for(int i=l+1;i<now;i++){
			if(all_gcd[i][now]>1){
				if(jud(l,i,now)){flag=true;} 
			}
		}   //能找到一个节点,作为now的左孩子,且左子树可行 
		vis_l[l+1][now+1]=true;
		result_l[l+1][now+1]=flag;
	}
	if(!vis_r[now+1][r+1]){   //右侧没有计算过 
		bool flagr=false;
		if(now==r-1){flagr=true;} 
		for(int i=now+1;i<r;i++){
			if(all_gcd[now][i]>1){
				if(jud(now,i,r)){flagr=true;} 
			}
		}   //能找到一个节点,作为now的右孩子,且右子树可行 
		vis_r[now+1][r+1]=true;
		result_r[now+1][r+1]=flagr;
	}
	return (result_l[l+1][now+1]&&result_r[now+1][r+1]);  
	 //如果左右子树都可行,now可做根 
}

void init(){
	for(int i=0;i<710;i++){
		for(int j=0;j<710;j++){
			vis_l[i][j]=0;		
			vis_r[i][j]=0;
			result_l[i][j]=0;
			result_r[i][j]=0;
		}
	}
}

int main(){
	scanf("%d",&t);
	for(int i=0;i<t;i++){
		init();
		scanf("%d",&n);
		for(int j=0;j<n;j++){
			scanf("%d",&a[j]);
		}
		for(int j=0;j<n;j++){
			for(int k=j;k<n;k++){
				all_gcd[j][k]=gcd(a[j],a[k]);
				all_gcd[k][j]=all_gcd[j][k];
			}
		}
		
		int flag=0;   //标志量,确定是否是break出来的 
		for (int j=0;j<n;j++)
		{
			if(jud(-1,j,n)){
				flag=1;
			}
		}
		if (flag==1){
			cout<<"Yes\n";
		}
		else{cout<<"No\n";}
	}
}

四、经验与总结

  1. 注意输出!!因为题目要输出Yes/No,而我输出YES/NO,几乎WA了一上午,一定要细心!注意输入输出格式!
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值