第一题
题目大意
统计一个数中不同的数字的个数,数的范围是1e15,最多有1e6个数。
思路
这个题的思路简单,就是将一个数的所有位上的数扫描一遍就行了,只是需要注意,用STL的set去重,会有超时的风险,所以可以用个数组记录一下。
代码
#include<bits/stdc++.h>
//这个题用STL有可能超时
using namespace std;
typedef long long llong;
llong a;
bool f[10];//开大一点
int main()
{
int n,k,cnt1=0,cnt=0;
scanf("%d%d",&n,&k);
for(int i=0;i<n;i++){
memset(f,0,sizeof f);
scanf("%lld",&a);
cnt1=0;
if(a==0) f[0]=1,cnt1++;
else{
while(a>0){
if(f[a%10]==0) f[a%10]=1,cnt1++;
a/=10;
}
}
if(cnt1<k) cnt++;
}
printf("%d",cnt);
return 0;
}
第二题
题目大意
给出坐标系中的若干点,以其中某个点为圆心,做圆,要求覆盖所有给出的点,并且圆形的面积最小。
思路
面积最小,即为半径最小,这个圆的半径取决于圆心与距离圆心最远的点之间的距离,所以只需要遍历一遍所有的点,找出这些点做为圆心时的半径,并找出最小的半径即可。
代码
注意这个题需要保留小数位数,所以使用double(这个题最大的数据是
8e10,double可以表示)
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
struct point{
double x,y;
bool operator<(const point & pp) const{
return x==pp.x? y<pp.y :x<pp.x;
}
}p[2000];
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%lf%lf",&p[i].x,&p[i].y);
}
sort(p,p+n);
double d=0,r2=1e11;
double dx=0,dy=0;
for(int i=0;i<n;i++){
d=0;
for(int j=0;j<n;j++){
if(i!=j){
d = max(d,(p[i].x-p[j].x)*(p[i].x-p[j].x)+(p[i].y-p[j].y)*(p[i].y-p[j].y));
}
}
if(r2>d){
r2=d;
dx = p[i].x;
dy = p[i].y;
}
}
printf("%.2lf %.2lf\n",dx,dy);
printf("%.2lf",r2);
return 0;
}
第三题
题目大意
给出一个升序序列,要求判断能否构建一颗二叉搜索树,且同时任意树边相连的两个节点的gcd(greatest common divisor)都超过1。
思路
因为要构建一棵二叉搜索树 那么如果一个点做根 左边的所有一定比它小 右边的所有一定比它大 而恰好输入就满足这样的顺序,而且,任意一棵子树中的节点 在输入的序列上一定是连续的,所以就形成了区间的概念,问题也转换为如何选择区间,所以可以使用区间DP
状态:
f
[
i
]
[
j
]
:
区
间
[
i
,
j
]
可
以
构
成
满
足
要
求
的
二
叉
搜
索
树
f[i][j]:区间[i,j]可以构成满足要求的二叉搜索树
f[i][j]:区间[i,j]可以构成满足要求的二叉搜索树
f
l
[
i
]
[
k
]
:
以
k
为
根
,
左
子
树
是
i
到
k
−
1
fl[i][k]:以k为根,左子树是i到k-1
fl[i][k]:以k为根,左子树是i到k−1
f
r
[
k
]
[
j
]
:
以
k
为
根
,
右
子
树
是
k
+
1
到
j
fr[k][j]:以k为根,右子树是k+1到j
fr[k][j]:以k为根,右子树是k+1到j
转移方程:
f
[
i
]
[
j
]
=
f
l
[
i
]
[
k
]
&
f
r
[
k
]
[
j
]
f[i][j]=fl[i][k]\&fr[k][j]
f[i][j]=fl[i][k]&fr[k][j]
在上式成立的情况下:
i
f
(
l
>
1
&
G
C
D
[
k
]
[
l
−
1
]
>
1
)
f
r
[
l
−
1
]
[
r
]
=
1
if(l>1\&GCD[k][l-1]>1) fr[l-1][r]=1
if(l>1&GCD[k][l−1]>1)fr[l−1][r]=1
这个式子的意思是:以这个可行的a[k]为子树根,这个子树来作为l-1的右子树, 也是相当于准备好了区间为len+1的情况,下式同理。
i
f
(
r
<
n
&
G
C
D
[
k
]
[
r
+
1
]
>
1
)
f
l
[
l
]
[
r
+
1
]
=
1
if(r<n\&GCD[k][r+1]>1) fl[l][r+1]=1
if(r<n&GCD[k][r+1]>1)fl[l][r+1]=1
代码
这个题要注意的是,GCD要提前算出来,不要用一次调一次函数,那样会超时
#include<bits/stdc++.h>
using namespace std;
typedef long long llong;
llong gcd(llong a,llong b){return b == 0 ? a : gcd(b,a%b);}
llong GCD[800][800];//必须要提前算,否则超时!!!
llong a[800];
//fl[i][k]:以k为根,左子树是i到k-1:i---k-1->k
//fr[k][j]:以k为根,右子树是k+1到j,k<-k+1---j
//f[i][j]=fl[i][k]&fr[k][j],i----k-1->k<-k+1-----j
bool f[800][800],fl[800][800],fr[800][800];
int n,t;
bool jdg(){
for(int len=1;len<=n;len++){
for(int i=1;i<=n-len+1;i++){ //左端点
int l =i , r=i+len-1;//一个长度为len区间[l,r]
for(int k=l;k<=r;k++){
if(fl[l][k]&&fr[k][r]){
f[l][r]=1;
if(l>1&&GCD[k][l-1]>1) fr[l-1][r]=1;
// 以这个可行的a[k]为子树根,这个子树来作为l-1的右子树,
//也是相当于准备好了区间为len+1的情况
if(r<n&&GCD[k][r+1]>1) fl[l][r+1]=1;
// 以这个可行的a[k]为子树根,这个子树来作为r+1的左子树
}
}
}
}
if(f[1][n]) printf("Yes\n");
else printf("No\n");
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
for(int j=0;j<=n;j++){
for(int i=0;i<=n;i++) {
if(i==j) fl[i][i]=fr[i][i]=1;
else f[i][j]=fr[i][j]=fl[i][j]=0;
GCD[i][j]=gcd(a[i],a[j]);
}
}
jdg();
}
return 0;
}