1.最少操作次数
有一个整数 A=2021,每一次,可以将这个数加 1 、减 1 或除以 2,其中除以 2 必须在数是偶数的时候才允许。
例如,2021 经过一次操作可以变成 2020、2022。
再如,2022 经过一次操作可以变成 2021、2023 或 1011。
请问,2021 最少经过多少次操作可以变成 1。
类似最短路径和最少操作次数这样的题都可以用bfs来求解
答案:14
分析:
为什么想到用BFS呢?
答:因为bfs就是从一个点出发,在当前位置上可以有上下左右四种走法;而这道题是从2021出发,可以有+1,-1,/2三种走法,所以是同类型的题,我们可以用bfs来求解
步骤:
1.创建结构体,存储状态,这里的状态有数字和操作数两个变量
2.设置bool数组用来记录已经走过的状态
代码:
#include<iostream>
#include<queue>
using namespace std;
typedef struct node{
int n,cnt;
}P;
bool vis[10000];
void bfs(){
queue<P> q;
P p = {2021,0};
q.push(p);
vis[2021] = true;
while(!q.empty()){
p = q.front();
q.pop();
P tmp;
if(p.n == 1){
cout << p.cnt;
return;
}
if(!vis[p.n+1]){
vis[p.n+1] = true;
tmp = {p.n+1,p.cnt+1};
q.push(tmp);
}
if(!vis[p.n-1]){
vis[p.n-1] = true;
tmp = {p.n-1,p.cnt+1};
q.push(tmp);
}
if(p.n % 2 == 0 && !vis[p.n/2]){
vis[p.n/2] = true;
tmp = {p.n/2,p.cnt+1};
q.push(tmp);
}
}
}
int main(){
bfs();
return 0;
}
2.超大玉螺旋丸
对于一个 n 行 m 列的表格,我们可以使用螺旋的方式给表格依次填上正整数,我们称填好的表格为一个螺旋矩阵。
例如,一个 4 行 5 列的螺旋矩阵如下:
1 2 3 4 5
14 15 16 17 6
13 20 19 18 7
12 11 10 9 8
请问,一个 30 行 30 列的螺旋矩阵,第 20 行第 20 列的值是多少?
分析:
我们可以把它分成一个个矩形来做。图中圆圈处的数是外圈和内圈的 交界处
每一个矩形的四个顶点的坐标如下图( i 表示圈数):
一共有30行,说明有15个矩形,第20行是在下半部分,代入n-i+1得,i = 11;第20行第20列处的值刚好在顶点处。我们只要算得第10圈交界处的值,再加19即可
第一圈交界处的值是:30*2+30*2-4(4个顶点重复算了一遍)
第二圈交界处的值是:30*2+30*2-4 + 28*2+28*2-4
……
叠加计算即可
代码:
#include<iostream>
using namespace std;
int main(){
int n = 30,m = 30;
int cnt = 0;
for(int i = 0;i < 10;i++){
cnt += n*2+m*2-4;
n-=2;
m-=2;
}
cout << cnt + 19;
return 0;
}
3.二叉树的最大深度
一棵二叉树有2021个结点。该树满足任意结点的左子树结点个数和右子树的结点个数之差最多为1。
定义根结点的深度为0,子结点的深度比父结点深度多1。
请问,树中深度最大的结点的深度最大可能是多少?
答案:10(如果有错,还望在评论区纠正)
分析:
多分析几个案例,我们可以发现:二叉数的最大深度 = 除法操作的次数
当n-1是奇数时,n/=2;
当n-1为偶数时,n = (n-1) / 2;
代码:
#include<iostream>
using namespace std;
int main(){
int n = 2021;
int cnt = 0;
while(n!=1){
if((n-1) % 2 != 0){
n/=2;
}else{
n = (n-1)/2;
}
cnt++;
}
cout << cnt;
return 0;
}
4.插座
小蓝有一个插板,形状用一个 n * m 的01矩阵表示,0 表示板面,1 表示插孔。
小蓝还有一个插头,形状用一个 r * c 的01矩阵表示,0 表示没有伸出的部分,1 表示伸出的部分。插头伸出的部分必须插在插孔里面。
为了安全,插头插到面板上不能有任何部分超过插板边界(包括没有伸出的部分)。
插头和插板都不能旋转,也不能翻转。请求出插头插入插板的合理位置。输入格式
输入的第一行包含两个整数 n, m。
接下来 n 行,每行一个长度为 m 的01串,表示插板的形状。
接下来一行包含两个整数 r, c。
接下来 r 行,每行一个长度为 c 的01串,表示插头的形状。输出格式
如果插头没办法安全插入插板中,输出“NO”。否则输出两个数 a, b,表示插头的第 1 行第 1 列对应插板的第 a 行第 b 列。如果有多种情况满足要求,输出 a 最小的方案,如果 a 最小的方案有多个,输出在 a 最小的前提下 b 最小的方案。
样例输入
3 4
0110
0000
0000
3 3
000
010
000样例输出
NO
样例说明
在插头不超出范围的前提下无法插入。
样例输入
4 7
1110100
1101111
0001111
0000011
2 3
111
011样例输出
2 4
评测用例规模与约定
对于 50% 的评测用例,2 <= n, m, r, c <= 20。
对于所有评测用例,2 <= n, m, r, c <= 100。
分析:
1.枚举插头在插板上的起始位置(注意插头的长和宽在插板上不能有任何越界),因为行是依次增大的,列也是依次增大的,所以搜索到的第一个符合条件的位置就是我们的答案
2.插头为‘1’的位置,插板必须为'1',插头为'0'的位置,插板可以是'1'也可以是'0'
代码:
#include<iostream>
using namespace std;
const int N = 110;
int n,m;
char s[N][N];
int r,c;
char d[N][N];
bool check(int a,int b){
for(int i = 0;i < r;i++){
for(int j = 0;j < c;j++){
if(d[i][j]=='1' && s[a+i][b+j]=='0'){
return false;
}
}
}
return true;
}
int main(){
cin >> n >> m;
for(int i=0;i < n;i++){
cin >> s[i];
}
cin >> r >> c;
for(int i=0;i<r;i++){
cin >> d[i];
}
//枚举起始位置
for(int a = 0;a+r-1 < n;a++){
for(int b = 0;b+c-1 < m;b++){
if(check(a,b)){
cout << a+1 << " " << b+1;
return 0;
}
}
}
cout << "NO";
return 0;
}
5.公约数
问题描述
给定正整数 a, b, c,请问有多少个正整数,是其中至少两个数的约数。
输入格式
输入一行包含三个正整数 a, b, c。
输出格式
输出一行包含一个整数,表示答案。
样例输入
30 70 35
样例输出
6
样例说明
1、2、5、7、10、35满足条件。
评测用例规模与约定
对于 50% 的评测用例,1 <= a, b, c <= 1000000。
对于所有评测用例,a, b, c 不超过 10**12(10的12次方)。
分析:
这道题还是比较简单的。
1.确定约数的范围。最小是1,最大不能超过a,b,c当中最大的那一个数
2.至少是两个数的约数,说明只要满足是a,b,c当中任意两个组合的约数,就算一种。
我们不需要考虑是a,b,c三个数的约数这种情况,因为如果k是a,b,c的约数,那么k一定也是a,b的约数或b,c的约数或a,c的约数
代码:
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
long long a,b,c;
cin >> a >> b >> c;
long long ans= 0 ;
for(int i = 1;i <= max(a,max(b,c));i++){
if(a%i==0 && b%i==0){
ans++;
continue;
}
if(b%i==0 && c%i==0){
ans++;
continue;
}
if(a%i==0 && c%i==0){
ans++;
}
}
cout << ans;
return 0;
}