TT数鸭子

题目大意

给定n,k以及n个数,判断n个数中有多少个数的数位中不同数字个数小于k。

输入

输入第一行包含两个数n,k,表示鸭子的个数和题目要求的k。
接下来一行有n个数,android 数鸭子 数鸭子玩法_android 数鸭子,每个数表示鸭子被TT映射之后的值。

输出

输出一行,一个数,表示满足题目描述的鸭子的个数。

数据范围

数据点

基本思路

逐位读入数字,利用数组或set统计即可。

完整代码

#include<iostream>
#include<string>
using namespace std;
int n, k, res;
int isok(const string &num)
{
	if(k>10)	return 1;
	int nbr[11]={0};
	int c=0;
	for(int i=0, l=num.length();i<l;i++)
	{
		if(!nbr[num[i]-'0'])
		{
			c++;
			nbr[num[i]-'0']=1;
		}
	}
	if(c<k)	return 1;
	return 0;	
}
int main ()
{
	ios::sync_with_stdio(false);
	cin>>n>>k;
	string tmp;
	for(int i=0;i<n;i++)
	{
		cin>>tmp;
		res += isok(tmp);
	}
	cout<<res<<endl;
	return 0;
}

ZJM要抵御宇宙射线

题目大意

二维平面上,给定N个点(每个点x,y均为整数),需要以其中一个点位圆心画圆,圆要把所有点都包括进来。求半径最小的圆的圆心坐标以及半径的平方。

输入

第一行一个正整数N,表示宇宙射线发射点的个数。
接下来N行,每行两个整数X,Y,表示宇宙射线发射点的位置。

输出

输出包括两行:
第一行输出保护罩的中心坐标x,y 用空格隔开;
第二行输出保护罩半径的平方(所有输出保留两位小数,如有多解,输出x较小的点,如扔有多解,输入y较小的点)。
无行末空格。

数据范围

数据点

基本思路

暴力穷举所有情况即可。即穷举所有的圆心位置,对于每个圆心计算与其他所有点的距离,从而找到最小半径。

完整代码

#include<iostream>
using namespace std;
int N;
long long x[1005], y[1005];
long long resX, resY;
long long res;
long long getRes(int i)
{
	long long r=-1;
	long long d;
	for(int j=0;j<N;j++)
	{
		if(j == i)	continue;
		d = (x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]);
		if(d>r)	r=d;
	}
	return r;
}
int main ()
{
	cin>>N;
	for(int i=0;i<N;i++)	cin>>x[i]>>y[i];
	resX=x[0];
	resY=y[0];
	res=getRes(0);
	for(int i=1;i<N;i++)
	{
		long long r=getRes(i);
		if(r<res)	
		{
			res=r;
			resX = x[i];
			resY = y[i];
		}
		else if(r == res)
		{
			if(x[i]<resX)
			{
				resX = x[i];
				resY = y[i];
			}
			else if(x[i] == resX && y[i] < resY)
			{
				resX = x[i];
				resY = y[i];
			}
		}
	}
	cout<<resX<<".00 "<<resY<<".00"<<endl;
	cout<<res<<".00"<<endl;
	return 0;
}

宇宙狗的危机

题目大意

给定n个升序排列的数,这n个数是升序排列的,问这n个数是否可以构成一棵二叉搜索树,并且树种每条边的两端点最大公约数都大于1。
求最大公约数的代码如下:

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

输入

输入第一行一个t,表示数据组数。 对于每组数据,第一行输入一个n,表示数的个数 接下来一行有n个数android 数鸭子 数鸭子玩法_android 数鸭子,输入保证是升序的。

输出

每组数据输出一行,如果能够造出来满足题目描述的树,输出Yes,否则输出No。无行末空格。

数据范围

数据点

android 数鸭子 数鸭子玩法_android 数鸭子

android 数鸭子 数鸭子玩法_android 数鸭子

android 数鸭子 数鸭子玩法_android 数鸭子

android 数鸭子 数鸭子玩法_android 数鸭子

android 数鸭子 数鸭子玩法_android 数鸭子

基本思路

这是一个区间dp问题,状态及转移如下所示:

状态

:a[l+1]-a[r]是否可以作为l的右子树;:a[l]-a[r-1]是否可以作为r的左子树

初始化

转移过程

维护两个数组lreach和rreach,lreach[l][r]表示ldp[l][r]是否被更新过,rreach[l][r]表示rdp[l][r]是否被更新过。
利用lreach和ldp可以实现lquery(l,r)函数,该函数返回a[l+1]-a[r]是否可以作为l的右子树,实现方法如下所示:

bool lquery(int l, int r)  //闭区间 
{
	if(!lreach[l][r])
	{
		lreach[l][r] = true;
		for(int i=l+1;i<=r;i++)
		{
			if(rquery(l+1,i) && lquery(i, r) && gcd(a[i], a[l]) > 1)
			{
				ldp[l][r] = true;
				break;
			}
		}
	}
	return ldp[l][r];
}

同理可以实现rquery(l,r),利用这两个函数便可以判断一个n个数是否可以组成二叉搜索树,方式如下:

bool query()
{
	for(int i=0;i<n;i++)
	{
		if(rquery(0, i) && lquery(i, n-1))	return true;	
	}
	return false;
}

在规定的时间内想到区间dp并找到合适的状态和转移可能难度不小,使用记忆化的方法尝试构造二叉搜索树也是一个较好的选择,可以拿到50-60分。

完整代码(区间dp)

#include<iostream>
using namespace std;
int a[750];
bool ldp[750][750], rdp[750][750];
bool lreach[750][750], rreach[750][750];
int t, n;
int gcd(long long a, long long b){return b == 0 ? a : gcd(b,a%b);}
bool rquery(int l, int r);
bool lquery(int l, int r)  //闭区间 
{
	if(!lreach[l][r])
	{
		lreach[l][r] = true;
		for(int i=l+1;i<=r;i++)
		{
			if(rquery(l+1,i) && lquery(i, r) && gcd(a[i], a[l]) > 1)
			{
				ldp[l][r] = true;
				break;
			}
		}
	}
	return ldp[l][r];
}

bool rquery(int l, int r)  //闭区间 
{
	if(!rreach[l][r])
	{
		rreach[l][r] = true;
		for(int i=l;i<=r-1;i++)
		{
			if(rquery(l,i) && lquery(i, r-1) && gcd(a[i], a[r]) > 1)
			{
				rdp[l][r] = true;
				break;
			}
		}
	}
	return rdp[l][r];	
}

bool query()
{
	for(int i=0;i<n;i++)
	{
		if(rquery(0, i) && lquery(i, n-1))	return true;	
	}
	return false;
}
int main ()
{
	cin>>t;
	for(int k=0;k<t;k++)
	{
		cin>>n;
		for(int i=0;i<n;i++)	cin>>a[i];
		for(int i=0;i<=n;i++)
		{
			for(int j=0;j<=n;j++)
			{
				lreach[i][j] = ldp[i][j] = rreach[i][j] = rdp[i][j] = false;
			}
		}
		for(int i=0;i<n;i++)
		{
			lreach[i][i] = ldp[i][i] = rreach[i][i] = rdp[i][i] = true;
		}
		if(query())	cout<<"Yes"<<endl;
		else cout<<"No"<<endl;
	}
	return 0;
}

完整代码(构造 + 记忆化数组)

#include<iostream>
using namespace std;
long long a[800];
int gcd(long long a, long long b){return b == 0 ? a : gcd(b,a%b);}
int n;
bool ok[500][500][500];
bool get[500][500][500];
bool isok(int root, int l, int r)
{
	if(get[l][r][root])
	{
		return ok[l][r][root];
	}
	bool lf=false, rf=false;
	if(root == l)	lf=true;
	else
	{
		for(int i=l;i<root;i++)
		{
			if(isok(i,l,root) && gcd(a[i], a[root]) > 1)
			{
				lf=true;
				break;
			}
		}
	}
	
	if(root == r-1)	rf=true;
	else
	{
		for(int i=root+1;i<r;i++)
		{
			if(isok(i,root+1,r) && gcd(a[i], a[root]) > 1)
			{
				rf=true;
				break;
			}
		}
	}
	get[l][r][root] = true;
	ok[l][r][root] = rf && lf;
	if(rf && lf)	
	{	
		return true;
	}
	else return false;
}
int main ()
{
	int t;
	cin>>t;
	for(int i=0;i<t;i++)
	{
		cin>>n;
		for(int i=0;i<n;i++)	cin>>a[i];
		for(int i=0;i<=n;i++)
		{
			for(int j=0;j<=n;j++)
			{
				for(int k=0;k<=n;k++)
				{
					get[i][j][k] = ok[i][j][k] = false;
				}
			}
		}
		bool flag = false;
		for(int i=0;i<n;i++)
		{
			if(isok(i, 0, n))
			{
				flag=true;
				break;
			}
		}
		if(flag)	cout<<"Yes"<<endl;
		else cout<<"No"<<endl;
	}
	return 0; 
}