蓝桥杯刷题第十九天

第一题:灌溉

题目描述
小蓝负责花园的灌溉工作。
花园可以看成一个 nm 列的方格图形。中间有一部分位置上安装有出水管。
小蓝可以控制一个按钮同时打开所有的出水管,打开时,有出水管的位置可以被认为已经灌溉好。
每经过一分钟,水就会向四面扩展一个方格,被扩展到的方格可以被认为已经灌溉好。即如果前一分钟某一个方格被灌溉好,则下一分钟它上下左右的四个方格也被灌溉好。
给定花园水管的位置,请问 k 分钟后,有多少个方格被灌溉好?
输入描述
输入的第一行包含两个整数 n,m
第二行包含一个整数 t,表示出水管的数量。
接下来 t 行描述出水管的位置,其中第 i 行包含两个数 r,c 表示第 r 行第 c 列有一个排水管。
接下来一行包含一个整数 k
其中,1≤n,m≤100,1≤t≤10,1≤k≤100。
输出描述
输出一个整数,表示答案。
输入输出样例
输入
3 6
2
2 2
3 4
1
输出
9

dfs思路,就是每次把有水的地方进行扩大

并且设置状态s,表示是第几次开始扩大的,这样每一次就能做到该次只扩大该一次的

#include<iostream>
#include<queue>
using namespace std;
 
const int N = 110;
int n, m, t, k;
int g[N][N];
int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, -1, 1};

void dfs(int r, int c, int s){
  for(int i = 0; i < 4; i++){
    int x = dx[i] + r, y = dy[i] + c;
    if(x >= 1 && x <= n && y >= 1 && y <= m && g[x][y] == 0)
      g[x][y] = s + 1;
  }
}

int main(){
  scanf("%d%d", &n, &m);
  scanf("%d", &t);

  while(t--){
    int a, b;
    scanf("%d%d", &a, &b);
    g[a][b] = 1;
  }

  scanf("%d", &k);
  int s = 1;
  while(s <= k){
    for(int i = 1; i <= n; i++)
      for(int j = 1; j <= m; j++)
        if(g[i][j] == s) {
          dfs(i, j, s);
        }
    s++;
  }

  int ans = 0;
  for(int i = 1; i <= n; i++)
    for(int j = 1; j <= m; j++)
      if(g[i][j] >= 1)
        ans++;

  cout<<ans<<endl;
  return 0;
}

第二题:小朋友崇拜圈

题目描述
班里 N 个小朋友,每个人都有自己最崇拜的一个小朋友(也可以是自己)。
在一个游戏中,需要小朋友坐一个圈,每个小朋友都有自己最崇拜的小朋友在他的右手边。
求满足条件的圈最大多少人?
小朋友编号为1,2,3,⋯N
输入描述
输入第一行,一个整数 N(3<N<10 5 )。
接下来一行 N 个整数,由空格分开。
输出描述
要求输出一个整数,表示满足条件的最大圈的人数。
输入输出样例
输入
9
3 4 2 5 3 8 4 6 9
输出
4

dfs,状态有三个,当前位置,当前位置的崇拜者,人数

枚举每个位置

当人数大于总人数肯定是死循环了,return

当崇拜者等于枚举的当前位置,即构成一个崇拜圈

更新最大值

其余情况继续dfs

#include<iostream>
using namespace std;

const int N = 1e5 + 10;
int n, p[N];
int ans;

void dfs(int u, int v, int t){
  if(t > n) return ;
  if(v == u) {
    ans = max(ans, t);
    return ;
  }
  dfs(u, p[v], t + 1);
}


int main(){
  scanf("%d", &n);

  for(int i = 1; i <= n; i++) scanf("%d", &p[i]);

  for(int i = 1; i <= n; i++)
    dfs(i, p[i], 1);

  cout<<ans<<endl;
  return 0;
}

第三题:括号序列

题目描述
给定一个括号序列,要求尽可能少地添加若干括号使得括号序列变得合法,当添加完成后,会产生不同的添加结果,请问有多少种本质不同的添加结果。
两个结果是本质不同的是指存在某个位置一个结果是左括号,而另一个是右括号。
例如,对于括号序列 ((()(((),只需要添加两个括号就能让其合法,有以下几种不同的添加结果:()()()()()()、()(())()(())、(())()(())()、(()())(()()) 和 ((()))((()))。
输入描述
输入一行包含一个字符串 s,表示给定的括号序列,序列中只有左括号和右括号。
输出描述
输出一个整数表示答案,答案可能很大,请输出答案除以 1000000007 (即 109+7)的余数。
输入输出样例
输入
((()
输出
5

括号问题两个条件:

  1. 左括号等于右括号

  1. 左括号一定大于等于右括号的数量才能加

独立性:放置左右括号方案互相不影响

所以可以先左括号放,再进行右括号放置(相当于反着放左括号),这里翻转加左右变换得到反序列

状态:f[i,j]表示前i个括号,左括号比右括号多j个的方案数量

然后方案数量 f[0,0] = 1

集合:左括号,f[i][j] = f[i-1][j-1];

右括号,f[i][j] = f[i-1][j+1] + f[i][j-1]

还要取余MOD

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

const int N =  5010, MOD = 1e9 + 7;
typedef long long LL;
char str[N];
LL f[N][N], n;

LL call(){
  memset(f, 0, sizeof f);
  f[0][0] = 1;
  for(int i = 1; i <= n; i++){
    if(str[i] == '('){
      for(int j = 1; j <= n; j++)
        f[i][j] = f[i-1][j-1];
    }
    else{
      f[i][0] = (f[i-1][0] + f[i-1][1]) % MOD;
      for(int j = 1; j <= n; j++)
        f[i][j] = (f[i-1][j+1] + f[i][j-1]) % MOD;
    }
  }

  for(int i = 0; i <= n; i++)
    if(f[n][i])
      return f[n][i];

  return -1;
}

int main(){
  scanf("%s", str + 1);

  n = strlen(str + 1);
  LL l = call();

  reverse(str + 1, str + 1 + n);
  for(int i = 1; i <= n; i++)
    if(str[i] == '(') str[i] = ')';
    else str[i] = '(';
  
  LL r = call();

  cout<<l * r % MOD<<endl;
  return 0;
}

第四题:砍竹子

砍竹子 - 蓝桥云课 (lanqiao.cn)

每次变为sqrtl(x/2+1),1e18只需要6次即可

思维题

考虑同一层的相邻的,是不是总次数就要减一

所以先计算每一课树,需要多少次,也就是多少层

再把相邻的减去

这里记住sqrt函数

sqrtl 计算long

sqrt 计算double

sqrtf 计算 float

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

const int N = 200010, M = 10;
typedef long long LL;
LL f[N][M];
int n, m;

int main(){
  scanf("%d", &n);
  LL stk[N];

  int res = 0;
  for(int i = 0; i < n; i++){
    LL x;
    scanf("%lld", &x);
    int top = 0;

    while(x > 1) stk[ ++ top] = x, x = sqrtl(x / 2  + 1);
    res += top;
    m = max(m, top);

    for(int j = 0, k = top; k ; j++, k--)
      f[i][j] = stk[k];
  }

  for(int i = 0; i < m; i++)
    for(int j = 1; j < n; j++)
      if(f[j][i] && f[j][i] == f[j-1][i])
        res--;

  cout<<res<<endl;
  return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Libert_AC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值