蓝桥杯2023年真题(3)

1.冶炼金属(二分、数学)

蓝桥杯2023年真题(3)_第1张图片

//二分
#include 
using namespace std;

int get1(int a, int b){
  int l = 0, r = 1e9;
  while(l + 1 < r){
    int mid = (l + r) / 2;
    if(a / mid <= b) r = mid;
    else l = mid;
  }
  return r;
}

int get2(int a, int b){
  int l = 0, r = 1e9;
  while(l + 1 < r){
    int mid = (l + r) / 2;
    if(a / mid < b) r = mid;
    else l = mid;
  }
  return l;
}

int main() {
  int n;
  scanf("%d", &n);
  int a, b;
  int res1 = -1e9, res2 = 1e9;
  for (int i = 0; i < n; i++) {
    scanf("%d%d", &a, &b);
    //当前区间的最小值
    res1 = max(res1, get1(a, b));
    //当前区间的最大值 = 下一个区间的最小值减1
    //res2 = min(res2, get1(a, b - 1) - 1);
    res2 = min(res2, get2(a, b));
  }
  printf("%d %d", res1, res2); 
  return 0;
}

//数学
// 由 a / v >= b, a / v < (b + 1)
// 得 v <= a / b, v > a / (b + 1)
#include 
using namespace std;

int main() {
  int n;
  scanf("%d", &n);
  int a, b;
  int res1 = -1e9, res2 = 1e9;
  for (int i = 0; i < n; i++) {
    scanf("%d%d", &a, &b);
    //左端点取最大值
    res1 = max(res1, a / (b + 1) + 1);
    //右端点取最小值
    res2 = min(res2, a / b);
  }
  printf("%d %d", res1, res2); 
  return 0;
}

2.飞机降落(dfs排列、状态压缩dp)

蓝桥杯2023年真题(3)_第2张图片
蓝桥杯2023年真题(3)_第3张图片

//dfs, O(n * n!)
#include
#include
using namespace std;
const int N = 15;
struct node{
  int a, b, c;
}p[N];
int t, n, st[N];

//当前第几个飞机,开始时间
int dfs(int u, int beg){
  if(u > n) return 1;
  for(int i = 1; i <= n; i++){
    int a = p[i].a, b = p[i].b, c = p[i].c;
    //如果当前飞机没被用过,且当前最晚降落时间大于等于上一个飞机结束的时间
    if(!st[i] && a + b >= beg){
      st[i] = 1;
      //当前出发的时间要是上一段结束,这一段开始,取靠后的
      if(dfs(u + 1, max(beg, a)+ c)) return 1;
      st[i] = 0;
    }
  }
  return 0;
}

int main(){
  scanf("%d", &t);
  while(t--){
    memset(st, 0, sizeof(st));
    scanf("%d", &n);
    int a, b, c;
    for(int i = 1; i <= n; i++){
      scanf("%d%d%d", &a, &b, &c);
      p[i] = {a, b, c};
    }
    if(dfs(1, 0)) printf("YES\n");
    else printf("NO\n");
  }
  return 0;
}

//dp, O(n * 2 ^ n)
#include
#include
using namespace std;
const int N = 20;
struct node{
  int a, b, c;
}p[N];
int t, n;
int f[1 << N]; //f[i]: 终点为第i位的最小值

int main(){
  scanf("%d", &t);
  while(t--){
    scanf("%d", &n);
    int a, b, c;
    for(int i = 0; i < n; i++){
      scanf("%d%d%d", &a, &b, &c);
      p[i] = {a, b, c};
    }
    memset(f, 0x3f, sizeof(f));
    f[0] = 0;
    //枚举所有情况, 0101表示放好了第一个和第三个飞机
    for(int i = 1; i < 1 << n; i++){
      for(int j = 0; j < n; j++){
        int a = p[j].a, b = p[j].b, c = p[j].c;
        //如果j位放好了飞机
        if(i >> j & 1){
          //上一个结束的时间
          int st = f[i - (1 << j)];
          if(a + b >= st) f[i] = min(f[i], max(st, a) + c);
        }
      }
    }
    if(f[(1 << n) - 1] == 0x3f3f3f3f) printf("NO\n");
    else printf("YES\n");
  }
  return 0;
}

3.接龙数(最长上升子序列dp)

蓝桥杯2023年真题(3)_第4张图片

//暴力枚举
#include 
using namespace std;
const int N = 1e5 + 10;
int n, a[N];
int main()
{
  scanf("%d", &n);
  for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
  int res = 0;
  //前一个数字
  for(int i = 1; i < n; i++){
    int x = a[i] % 10;
    //后一个数字
    for(int j = i + 1; j <= n; j++){
      int t = a[j], y;
      while(t){
        y = t % 10;
        t /= 10;
      }
      if(x != y){
        res++;
      }else{
        i = j - 1;
        break;
      }
    }
  }
  printf("%d", res);
  return 0;
}

//O(n ^ 2)
#include 
using namespace std;
const int N = 1e5 + 10;
int n, l[N], r[N];
int f[N]; //f[i]: 以a[i]结尾的接龙子序列长度最大值
int main()
{
  scanf("%d", &n);
  string s;
  for(int i = 1; i <= n; i++){
    cin>>s;
    l[i] = s[0] - '0';
    r[i] = s[s.size() - 1] - '0';
  }
  int res = 0;
  //右边界
  for(int i = 1; i <= n; i++){
    f[i] = 1;
    //左边界
    for(int j = 1; j < i; j++){
      if(l[i] == r[j]) f[i] = max(f[i], f[j] + 1);
    }
    res = max(f[i], res);
  }
  printf("%d", n - res);
  return 0;
}

//O(n)
#include 
using namespace std;
const int N = 1e5 + 10;
int n, l[N], r[N];
int f[N], g[10]; //f[i]: 以a[i]结尾的接龙子序列长度最大值
int main()
{
  scanf("%d", &n);
  string s;
  for(int i = 1; i <= n; i++){
    cin>>s;
    l[i] = s[0] - '0';
    r[i] = s[s.size() - 1] - '0';
  }
  int res = 0;
  //右边界
  for(int i = 1; i <= n; i++){
    f[i] = 1;
    f[i] = max(f[i], g[l[i]] + 1);
    g[r[i]] = max(g[r[i]], f[i]);
    res = max(f[i], res);
  }
  printf("%d", n - res);
  return 0;
}

//最优!
要求使得数列变成接龙数列的最少删除个数, 相当于求该数列的最长接龙子数列的长度, 用总长度减去最长接龙长度即为最少删除个数。

定义dp[i][j]为前i个数中, 以数字j结尾的最长接龙数列的长度。

设第i个数的首位数字是a, 末位数字是b。 则dp[i]中相对于dp[i-1]可能发生变化的只有dp[i][b], 因为第i个数只可能加到一个以a结尾的接龙数列中, 使得这个接龙数列长度加1并且结尾数字变成b.

所以状态转移方程为dp[i][b] = max(dp[i - 1][b], dp[i - 1][a] + 1)

而显然第一维可以优化掉。

#include 
using namespace std;
int f[10]; //f[j]: 以数字j结尾的最长接龙子序列长度
int main()
{
  int n, res = 0;
  scanf("%d", &n);
  string s;
  for(int i = 1; i <= n; i++){
    cin>>s;
    //首尾数字
    int l = s[0] - '0', r = s[s.size() - 1] - '0';
    f[r] = max(f[r], f[l] + 1);
    res = max(res, f[r]);
  }
  printf("%d", n - res);
  return 0;
}

4.岛屿个数(dfs、bfs)

蓝桥杯2023年真题(3)_第5张图片

//dfs
#include
using namespace std;
const int N = 55;
char g[N][N];
int n, m;

int dx[] = {0, 0, -1, 1, 1, 1, -1, -1};
int dy[] = {1, -1, 0, 0, 1, -1, 1, -1};

void dfs1(int x, int y){
  g[x][y] = '2';
  for(int i = 0; i < 8; i++){
    int a = x + dx[i], b = y + dy[i];
    if(a < 0 || a > n + 1 || b < 0 || b > m + 1 || g[a][b] != '0') continue;
    dfs1(a, b);
  }
}

void dfs2(int x, int y){
  g[x][y] = '2';
  for(int i = 0; i < 4; i++){
    int a = x + dx[i], b = y + dy[i];
    if(a < 1 || a > n || b < 1 || b > m || g[a][b] == '2') continue;
    dfs2(a, b);
  }
}

int main(){
  int t;
  cin>>t;
  while(t--){
    int res = 0;
    cin>>n>>m;
    for(int i = 1; i <= n; i++)
      for(int j = 1; j <= m; j++)
        cin>>g[i][j];

    //往外面再扩展一圈,方便打标记
    for(int i = 0; i <= n + 1; i++) g[i][0] = g[i][m + 1] = '0';
    for(int j = 0; j <= m + 1; j++) g[0][j] = g[n + 1][j] = '0';

    //开始遍历,把最外面一圈海水打上标记
    dfs1(0, 0);

    for(int i = 1; i <= n; i++)
      for(int j = 1; j <= m; j++)
        if(g[i][j] == '1'){
          res++;
          //遍历所在岛屿和他内部,全部打上标记
          dfs2(i, j);
        }
  
    cout<<res<<endl;
  }
  return 0;
}

5.子串简写(逆序对、双指针)

蓝桥杯2023年真题(3)_第6张图片

#include 
using namespace std;
long long res, ans;

int main()
{
  int k;
  string s;
  char c1, c2;
  cin>>k>>s>>c1>>c2;
  //双指针,逆序对写法,左指针遇到一个就++,右指针累加,区间长度为k,一位一位移动
  for(int i = 0, j = k - 1; j < s.size(); i++, j++){
    if(s[i] == c1) res++;
    if(s[j] == c2) ans += res;
  }
  cout<<ans;
  return 0;
}

6.整数删除(优先队列、双链表)

蓝桥杯2023年真题(3)_第7张图片

#include
#include
using namespace std;
#define int long long
const int N = 5e5 + 10;
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q; // 小根堆,存储元素和下标

int l[N], r[N]; // 存储左右下标
int st[N]; // 存储变化后的值
int n, k, x;

signed main(){
  cin >> n >> k;
  for(int i = 0; i < n; i++){
    cin >> x;
    st[i] = x;
    q.push(make_pair(x, i));
    l[i] = i - 1;
    r[i] = i + 1;
    if(r[i] == n) r[i] = -1;
  }

  while(k){
    // 取出最小值
    pair<int, int> it = q.top();
    q.pop();
    // 如果当前的值不等于更新后的值,说明还没更新
    if(it.first != st[it.second]){
      q.push(make_pair(st[it.second], it.second));
      continue;
    }
    k--;

    // 获取该元素的下标和值
    int pos = it.second, x = it.first;
    // 加到左边
    if(l[pos] >= 0) st[l[pos]] += x;
    // 加到右边
    if(r[pos] >= 0) st[r[pos]] += x;

    // 更新左右指向
    if(l[pos] >= 0) r[l[pos]] = r[pos];
    if(r[pos] >= 0) l[r[pos]] = l[pos];

    // 删除当前元素
    st[pos] = -1;
  }

  // 输出结果
  for(int i = 0; i < n; i++){
    if(st[i] != -1) cout << st[i] << " ";
  }
  cout << endl;
  return 0;
}

你可能感兴趣的:(备战蓝桥杯,蓝桥杯,深度优先,职场和发展)