2018蓝桥杯省赛B组

2018蓝桥杯省赛B组

6.第几天

#include <iostream>
using namespace std;
int main()
{cout<<125;
  // 请在此输入您的代码
  return 0;
}
计算:
31+29+31+30+4=125

计算的代码


#include<bits/stdc++.h>
using namespace std;
int ans;
int main()
{
  
  int m[13]={0,31,29,31,30,31,30,31,31,30,31,30,31};//考:每个月天数的表示
  int y1=1;
  int r1=1;
 int y2=5;
int r2=4;
 while(!((y1==y2)&&(r1==r2))){
r1++;


if(r1>m[y1]){
  r1=1;
  y1++;
}

 ans++;

 
 }
 cout<<ans+1;
  // 请在此输入您的代码
  return 0;
}

A.明码

//第一步弄清楚这段文字是什么这是个汉字
#include<bits/stdc++.h>
using namespace std;
void two(int i,string&a){
//对于一个整数化成8位二进制,范围-128~127,对于正数最高位在二进制中是0也就是默认的-,对于负数最高位是1
if(i>=0){
for(int j=0;j<7;j++){
if((i>>j)&1==1){
a[8-j-1]='1';
}
}
}
else{//负数
a[0]='1';
for(int j=0;j<7;j++){
if(((128+i)>>j)&1==1){
a[8-j-1]='1';
}
}
}
}
int main(){
/*解析:题目中说有十个汉字,刚好十行,一行一个汉字!然后 一个汉字十六行,一行2个字节,所以一个汉字32个字节,刚好一行32个整数,所以一个整数
表示1个字节,所以我们的任务是将每个汉字的字形先输出按照一行两字节(2个整数-->化成2进制)一个汉字16行,然后通过汉字读懂题最后输出答案。
关键:将任意整数化成二进制
怎么看:1为墨迹要看的,-不管

*/
for(int i=0;i<10;i++){//10个整数
for(int j=0;j<16;j++){//1个整数16行,1行两字节(2个是十进制整数)
int x,y;
cin>>x>>y;
//先默认一行的两字节,等会儿只用看哪一位化成二进制是1,如果是0就不用变还是-
string a="--------";
string b="--------";
two(x,a);
two(y,b);
cout<<a+b<<endl;
}
cout<<"====================================================";//下一个汉字
}

return 0;
}
//第二步计算要提交的答案-->上面文字为9的9次方等于多少?
#include<bits/stdc++.h>
using namespace std;
int main(){
    long long int ans=1;
    for(int i=0;i<9;i++){
    ans*=9;
    }
    
    cout<<ans;
        return 0;
}

 

//填空题只用提交答案
#include<bits/stdc++.h>
using namespace std;
int main(){
    cout<<"387420489 ";
    return 0;
}

考点:

读懂题-->确定是考察化整数为二进制

化成二进制模版

 void two(int i,string&a){
//对于一个整数化成8位二进制,范围-128~127,对于正数最高位在二进制中是0也就是默认的-,对于负数最高位是1
if(i>=0){
for(int j=0;j<7;j++){
if((i>>j)&1==1){
a[8-j-1]='1';
}
}
}
else{//负数i和正数128+i只有最高位不同
a[0]='1';
for(int j=0;j<7;j++){
if(((128+i)>>j)&1==1){
a[8-j-1]='1';
}
}
}
}

B.乘积尾零

考点:

有点类似于试除法求约数

对于末尾为0可以考虑成2*5=10所以只需要计算所有输入的数可以分解成几个2几个5先分解2分解5都行,然后取因数为2,5个数最少的那个。

#include<bits/stdc++.h>
using namespace std;
int main(){
  int p[200];
  for(int i=0;i<100;i++){
    cin>>p[i];
  }
  int a=0;//有多少个2为因子
  int b=0;
for(int i=0;i<100;i++){
  int k=p[i];
  while(k%2==0){
    a++;
    k/=2;
  }
   while(k%5==0){
    b++;
    k/=5;
  }
}
  cout<<min(a,b);
  return 0;
}

递增三元组

考点:

1.stl二分

lower_bound( )//大于等于

upper_bound( )//大于

用法:

利用二分查找stl必须保证数组有序
1.查找第一个大于等于这个数
(1)得第一个大于等于这个数的数字下标
lower_bound(数组名+查找起点,数组名+查找终点+1,查找数)-数组名
(2)返回指向大于等于这个数的第一个数字的指针
lower_bound(数组名+查找起点,数组名+查找终点+1,查找数)
(3)返回指向大于等于这个数的第一个数字的指针
*lower_bound(数组名+查找起点,数组名+查找终点+1,查找数)
2.查找第一个大于这个数
(1)得第一个大于这个数的数字下标
upper_bound(数组名+查找起点,数组名+查找终点+1,查找数)-数组名
(2)返回指向大于这个数的第一个数字的指针
upper_bound(数组名+查找起点,数组名+查找终点+1,查找数)
(3)返回指向大于这个数的第一个数字的指针
*upper_bound(数组名+查找起点,数组名+查找终点+1,查找数)

例如:

#include<bits/stdc++.h>
using namespace std;
int main(){
    int a[5]={1,3,5,9,19};
    cout<<*upper_bound(a,a+5,5);
    return 0;
}

2.关键:

要将中间b数组作为参照,a数组中取比他小的有x个,c数组中取比他大有y个,那对于当前b中这个元素满足条件三元组有x*y个。

要乘法和stl否则超时

完整代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+6;
int a[N];
int b[N];
int c[N];
int main(){
int n;
cin>>n;
//1.输入a,b,c数组
for(int i=0;i<n;i++){
  cin>>a[i];
}
for(int i=0;i<n;i++){
  cin>>b[i];
}
for(int i=0;i<n;i++){
  cin>>c[i];
}
//2.排序
sort(a,a+n);
sort(b,b+n);
sort(c,c+n);
long long int ans=0;//答案怕太大所以long long
//3.以b做参考
for(int i=0;i<n;i++){
  //(1)第一个比b[i]大于等于的a中数的下标(我们不要的)也是比b[i]小的a里面数的个数
  int x=lower_bound(a,a+n,b[i])-a;
  //(2)第一个比b[i]大于的c中数的下标(我们要的)
  int y=n-(upper_bound(c,c+n,b[i])-c);
  ans+=x*y;
}
cout<<ans;
  return 0;
}

1.乘积最大

考双指针+分类讨论(可以尝试取数个数的奇偶来看)

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+6;
 int a[N];
const int mod=1e9+9;
typedef long long int ll;
int main(){
int n,k;
cin>>n>>k;
for(int i=0;i<n;i++){
  cin>>a[i];
}
sort(a,a+n);
/*
考的点是:分类讨论
1.如果k==n全取
2.k<n
(1)k是偶数
那么结果一定>=0
(2)k是奇数
此时如果全是负数那么最大乘积也是奇数
此时如果有非负数那么最大乘积>=0
综上所述:
我们可以将k为奇数情况的先将他的最大项拿出来,然后变为k为偶数的情况
对于最大乘积我们可以通过双指针考虑,左右两端同时取绝对值最大的两项相乘看哪个乘上当前符号结果最大就去哪个然后移动那端的指针(动两位)
为什么只考虑符号与之相乘就行
因为我们可以把当前结果为负数的用一个符号sign=-1表示
正数用sign=1
若x*sign>y*sign
那么x乘以当前值也比y乘当前值大!
*/
//双指针
int l=0;
int r=n-1;
//符号位
int sign=1;
ll res=1;
if(k%2){
res=a[r];
r--;
k--;
if(res<0)sign=-1;//只有取数个数为奇数个有特殊情况为负数的情况 单独看
}
while(k){
//注意处理long long
  ll x=(ll)a[l]*a[l+1];
  ll y=(ll)a[r]*a[r-1];
  if(x*sign>y*sign){//与对应符号相乘即可!
    res=x%mod*res%mod;//x可能暴long long
 
    l+=2;
  }
else{
   res=y%mod*res%mod;
    r-=2;
}
k-=2;
}
cout<<res;
return 0;
}

日志统计:

暴力+stl

思路:先用个容器存放每个id对应的多个时间点接收到,然后检验每个id是不是热帖

检验每个id是不是热帖:

首先对该id的发送时间数组进行从小到大排序,然后枚举有可能的该id满足条件的第一个发送时间,然后对从该发送时间往后数看有没有可能满足热帖条件,只要有一种开头满足就可以跳出这个循环并输出该id判断条件是个数满足k个在d时间范围内!

#include <bits/stdc++.h>
using namespace std;
map<int,vector<int> >a;
set<int>b;
int main()
{

int n,d,k;
cin>>n>>d>>k;
//将每个id对应的时间存进去 
while(n--){
  int t,id;
  cin>>t>>id;
  a[id].push_back(t);
  b.insert(id);
}
set<int>::iterator i;//如何赢迭代器访问set元素
int ans=0;
for( i=b.begin();i!=b.end();i++){
  int t=*i;//当前id
  int h;
  sort(a[t].begin(),a[t].end()) ;
  for(int j=0;j<a[t].size();j++ ){//当前id的开始时间 
     h=0;
    
    while(h<k&&a[t][j+h]-a[t][j]<d&&j+h<a[t].size()){
      h++;
    }
    if(h>=k){
  break;
    }
  }
  if(h>=k){
    cout<<t<<endl;
  }
  
}

  // 请在此输入您的代码
  return 0;
}
 

快速排序

#include<bits/stdc++.h>
using namespace std; 
 与我们所学快排是反着的参照数据放最右侧这道题目。
int quick_select(int a[], int l, int r, int k) {
    int p = rand() % (r - l + 1) + l;//生成一个[l,r]的下标 
    int x = a[p];//随机生成值 
    {int t = a[p]; a[p] = a[r]; a[r] = t;}//a[p]放最右边,参照值在右边 
    int i = l, j = r;
    while(i < j) {
        while(i < j && a[i] < x) i++;
        if(i < j) {
            a[j] = a[i];
            j--;
        }
        while(i < j && a[j] > x) j--;
        if(i < j) {
            a[i] = a[j];
            i++;
        }
    }
    a[i] = x;
    p = i;
    if(i - l + 1 == k) return a[i];//答案 

理由是右半边一定不是第k小的应该减去前面比他小的。

这个题考快速排序,将数列分成两部分,一部分比参照数据(随机数)小,一部分比他大。
    if(i - l + 1 < k) return quick_select( a,i,r,k-(i-l) ); //填空

    else return quick_select(a, l, i - 1, k);
}
int main()
{
    int a[] = {1, 4, 2, 8, 5, 7, 23, 58, 16, 27, 55, 13, 26, 24, 12};
    printf("%d\n", quick_select(a, 0, 14, 5));
    return 0;
}

全球变暖

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;

const int N = 1010;

int n;
char g[N][N];
bool st[N][N];
PII q[N * N];
int dx[4] = {-1, 0, 1, 0};
int dy[4] = {0, 1, 0, -1};
核心宽搜(队列)
void bfs(int sx, int sy, int &total, int &bound)
{
自己先入队,每个元素存放这个点横纵坐标所以用pair型
    int hh = 0, tt = 0;//hh队头,tt队尾
    q[0] = {sx, sy};
    st[sx][sy] = true;//访问这个点

    while (hh <= tt)
    {
        PII t = q[hh ++ ];

        total ++ ;
        bool is_bound = false;//默认不是边界
遍历四周看是不是临海
        for (int i = 0; i < 4; i ++ )
        {
            int x = t.x + dx[i], y = t.y + dy[i];
            if (x < 0 || x >= n || y < 0 || y >= n) continue;  // 出界
//不重复访问
            if (st[x][y]) continue;
//临海
            if (g[x][y] == '.')
            {
                is_bound = true;
                continue;
            }
//周边这个点不是海就算在连通块里(岛屿)
            q[ ++ tt] = {x, y};
            st[x][y] = true;
        }
//当前元素是边界(临海)
        if (is_bound) bound ++ ;
    }
}

int main()
{
1.输入地图
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ ) scanf("%s", g[i]);
2.计算被淹没的岛屿cnt
    int cnt = 0;
    for (int i = 0; i < n; i ++ )
        for (int j = 0; j < n; j ++ )
//每个点搜一遍,搜的条件是没被搜过且是陆地
            if (!st[i][j] && g[i][j] == '#')
            {
                int total = 0, bound = 0;
//从这个点开始搜,统计这个连通块(岛屿)的组成块数和临海边缘块数
                bfs(i, j, total, bound);
//统计这个岛屿的组成块数和临海边界块数,然后判断如果二者相等完全淹没
                if (total == bound) cnt ++ ;
            }

    printf("%d\n", cnt);

    return 0;
}
完整代码
 #include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n;
char g[N][N];//存放地图
bool st[N][N];//这个点是否被访问
#define x first
#define y second
typedef pair<int,int >PII;
PII q[N*N];//n*n个点,所以用n*n个组合
int ddx[4]={-1,0,0,1 };//x坐标变化(行)
int ddy[4]={0,-1,1,0};//y坐标变化(列)
void bfs(int sx,int sy,int &total,int &bound){
  q[0]={sx,sy};
  st[sx][sy]=true;//访问这个点
  int hh=0;//队头
  int tt=0;//队尾
  while(hh<=tt){
PII t=q[hh++];//取队头

total++;
bool is_bound =false;
//以队头为中心四个方向
for(int i=0;i<4;i++){
  int dx=t.x+ddx[i];
  int dy=t.y+ddy[i];
  //1.不重复访问
  if(st[dx][dy])continue;
  if(dx<0||dx>=n||dy<0||dy>=n)continue;//出界
  //如果是海
  if(g[dx][dy]=='.'){
    is_bound=true;
    continue;
  }
  //不是海,就加入到连通块里
  q[++tt]={dx,dy};
st[dx][dy]=true;//加入连通块同时被搜到了
}
if(is_bound)bound++;
  }
}


int main(){

cin>>n;
//存地图
for(int i=0;i<n;i++){
  for(int j=0;j<n;j++){
    cin>>g[i][j];
  }
}
//开始搜,计算被淹没岛屿数
int cnt=0;
for(int i=0;i<n;i++){
  for(int j=0;j<n;j++){
    
    if(!st[i][j]&&g[i][j]=='#'){
      int total=0;
    int bound =0;
      bfs(i,j,total,bound);
      if(total==bound)cnt++;
    }
  }
}
cout<<cnt;//被淹没的岛屿数
}
思路:

题目分析:

计算被淹没的岛屿数

首先确定考点搜索,将每个点搜一遍,但其实是对没有搜过的陆地进行宽搜看他所在岛屿(连通块)是否被淹没

是否被淹没看两个指标

即如果组成连通块的陆地数等于沿海边界数那么就是被淹没了统计上。

所以我们bfs(起点行坐标,起点列坐标,组成连通块的陆地数,沿海边界的陆地数)

这个bfs无返回值

对于这个bfs的写法

对于bfs他是依赖队列的,手写队列肯定要定义队头队尾,初始队头队尾均为0(下标)。因为要存放横纵坐标所以用pair型,define可以对first,second进行简写。

先将传入点放进队列,并访问st[][ ]=true;

然后就是while(hh<=tt){//队头下标小于等于队尾

取队头

组成该连通块的陆地数+1

队头是否是边界先默认false

对队头上下左右四个方向进行访问,

四种情况

出界 continue

访问过 continue

是海 那么这个队头沿海bool就要设置为true ,continue

最后是他也是陆地那么加进这个连通块里(入队),并且因为判断该他是陆地也就是搜索过了所以st[ ][ ]设置为true

如果沿海bool是true那么他就是边界bound++

}

测试次数

#include<bits/stdc++.h>
using namespace std;
//是dp问题考察状态转移
const int N=1010;
int f1[N];//1部手机在拥有不同层下的不同方案
int f2[N];//2部手机
int f3[N];
int main(){
  //1.拥有1部手机
for(int i=1;i<=1000;i++){
f1[i]=i;//只能从下往上扔(因为只有1部),在最坏运气下要测i次
}
//2.拥有2部手机
for(int i=1;i<=1000;i++){//2部手机在不同塔高下的测试数
f2[i]=1200;
for(int j=1;j<=i;j++){//为了找当前塔高的最佳策略所以把从1~i层扔的最坏运气下的方案数求出然后选最小就是最佳策略
//其实从哪一层扔都只有两种情况一种是扔完是好的,另一种是扔完坏了
int _max=max(f2[i-j]+1,f1[j-1]+1);
f2[i]=min(f2[i],_max);
}
}
//2.拥有3部手机
for(int i=1;i<=1000;i++){//3部手机在不同塔高下的测试数
f3[i]=1200;
for(int j=1;j<=i;j++){//为了找当前塔高的最佳策略所以把从1~i层扔的最坏运气下的方案数求出然后选最小就是最佳策略
//其实从哪一层扔都只有两种情况一种是扔完是好的,另一种是扔完坏了
int _max=max(f3[i-j]+1,f2[j-1]+1);
f3[i]=min(f3[i],_max);
}

}
cout<<f3[1000];
return 0;
}
考点分析:
动态规划
做法找规律,他不是贪心不一定中间最好
关键理解最坏运气,最优策略
在塔高固定
可以在这之间的任意一层开扔
最坏运气:
对于任意一层开始扔都有两种情况,扔完是好的或者是坏的
好坏各对应一个方案数最坏运气指的是取二者中较大值作为从这一层扔的结果值
最优策略:
是在这所有层开始扔的方案数中取最小的
所以设置三个数组
有1部手机f1[N]
有两部手机f2[N]
有三部手机f3[N]
然后递推从小到大推
当只有1部手机是特殊情况只能从第一层一层层往上扔所以f1[i]=i;
然后两部手机,递推1~1000层的最优策略
找最优策略需要枚举1~i层从哪一层开扔最好,
最坏运气即max(f2[i-j]+1,f1[j-1]+1);即扔完最坏最好取最大作为最坏运气,为了计算塔高为i的最优策略2部手机从第j层扔扔完是好的那么还剩2部手机i-j层楼,扔完坏了,那还剩下面j-1 层楼还剩1部手机,记得这两种情况都要加1因为当前用了1次测试次数。
最优策略上面所有扔法min
最后3部手机同2部的方法
最后输出f3[1000]即3部手机1000层

螺旋折线

直接模拟会超时(50%)

所以要找规律

对于这种正方形的问题关键是四条边

分类讨论在每条边要满足的条件和当前这种情况的距离公式

关键就是正方形的四个顶点的距离表达公式要求出来,然后在哪条边再用它改造。、

按位置分分四类:

在上面 |x|<=y

在右边 |y|<=x

在下边 |x|<=|y|+1 &&y<=0  (为防止y>0)

在左边 |y|<=|x|

n指的是从内向外数的层数如(3,3)在第三层

四个角离原点距离公式

左上角 2*n*(2*n-1)

右上角 (2*n)*(2*n)

右下角 (2*n+1)*(2*n)

左下角 (2*n-1)*(2*n-1)

在四种不同边上点的距离公式

上    :

参照左上角的点

dis(x,y)=2*n*(2*n-1)+x-(-n)

 

下:

参考右下角的点

(2*n+1)*(2*n)+n-x

左:

参考左下角的点

(2*n-1)*(2*n-1)+y-(-(n-1))

 

右:

参考右上角的点

2*n*(2*n)+n-y

 计算层数:

 层数:由内向外数圈数如下图:

 

 

上面计算层数: n=y;
 
右边计算层数:n=x;
  
下边计算层数: n=abs(y);
 
左边计算层数: n=abs(x);
 
完整代码
#include<bits/stdc++.h>
using namespace std;
//因为最外圈一条边就10的9次方,所以会爆int
typedef long long int ll;
int main(){
	int x,y;
  ll n;//n为层数
  cin>>x>>y;//求dis(x,y)
  ll dis=0;
  if(abs(x)<=y){//上面
 n=y;
dis=(ll)2*n*(2*n-1)+x-(-n);
  }
  else if(abs(y)<=x){//右边
  n=x;
  dis=(ll)2*n*(2*n)+n-y;
}
else if(abs(x)<=abs(y)+1&&y<=0){
  //下边
   n=abs(y);
   dis=(ll)(2*n+1)*(2*n)+n-x;
}
else if(abs(y)<=abs(x)){//左边
n=abs(x);
dis=(ll)(2*n-1)*(2*n-1)+y-(-(n-1));
}

cout<<dis;
	return 0;
}

注意事项:

else if摆的顺序有要求,因为他是不满足上面那个才进行下面那个,如果满足了那么下面都不进行

观察右边和左边如果左边这个算式不满足那右边式子更不满足那不满足左边那右边就不会判断了,所以对于有右边的判断要放在左边的前面。

if(abs(x)<=y){//上面
 n=y;
dis=(ll)2*n*(2*n-1)+x-(-n);
  }
else if(abs(x)<=abs(y)+1&&y<=0){
  //下边
   n=abs(y);
   dis=(ll)(2*n+1)*(2*n)+n-x;
}
else if(abs(y)<=x){//右边
  n=x;
  dis=(ll)2*n*(2*n)+n-y;
}
else if(abs(y)<=abs(x)){//左边
n=abs(x);
dis=(ll)(2*n-1)*(2*n-1)+y-(-(n-1));
}
posted @   Annaprincess  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示