2121的题解

A - 信箱

  • 如果起点 s 在区间 [a[1], a[n]] 之外,直接移动到区间的远端。

  • 如果起点 s 在区间内,计算移动到区间左端点或右端点的最短距离,然后遍历整个区间。

  
  #include<iostream>
  #include<cstring>
  using namespace std;
  int n,s;
  int a[105];
  int main(){
    int t;
    cin>>t;
    while(t--){
      memset(a,0,sizeof(a));
      cin>>n>>s;
      int x;
      for(int i=1;i<=n;i++){
        cin>>a[i];
      }
      if(s>=a[n]||s<=a[1]){
        if(s>=a[n] )cout<<s-a[1]<<endl;
        else cout<<a[n]-s<<endl;
        continue;
      }
      int mi=min(s-a[1],a[n]-s);
      
      int ans=mi+a[n]-a[1];
      cout<<ans<<endl;
    }
  }

B - 天空之上

简化判断:关键观察:若存在合法拆分,最短的b(长度为 1)最容易满足子串条件。因为单个字符只要在a+c中出现过即可。因此,我们可以只检查b为单个字符的情况:

  • as[0](第一个字符),cs[2..n-1](从第三个字符到结尾),则bs[1](第二个字符)。此时只需检查s[1]是否在a+c(即s[0] + s[2..n-1])中出现。

  • 若上述情况不满足,再让as[0..n-3]bs[n-2]cs[n-1],检查s[n-2]是否在a+c中出现。

结论:若上述两种情况中任意一种满足,则输出Yes;否则输出No

  
  #include<iostream>
  #include<cstring>
  using namespace std;
  int n,s;
  int a[105];
  int main(){
    int t;
    cin>>t;
    while(t--){
      memset(a,0,sizeof(a));
      cin>>n>>s;
      int x;
      //int mx=0,mi=9999;
      for(int i=1;i<=n;i++){
        cin>>a[i];
      }
      if(s>=a[n]||s<=a[1]){
        if(s>=a[n] )cout<<s-a[1]<<endl;
        else cout<<a[n]-s<<endl;
        continue;
      }
      int mi=min(s-a[1],a[n]-s);
      
      int ans=mi+a[n]-a[1];
      cout<<ans<<endl;
    }
  }

C - 我的那些花儿

要解决这个问题,我们需要找到执行一次特定操作后矩阵中可能的最小最大值。操作定义为:选择一行r和一列c,将该行和该列的所有单元格值减一。核心目标是通过合理选择rc,使操作后矩阵的最大值尽可能小。

  1. 获取最大值及数量:遍历矩阵,找到最大值mx,并统计其出现的总次数cnt_mx

  2. 统计每行每列的最大值数量:用数组r[i]记录第i行中mx的数量,用数组c[j]记录第j列中mx的数量。

  3. 检查是否存在覆盖所有最大值的行和列:对每个单元格(i,j),计算 “第i行的mx数量 + 第j列的mx数量 - 重复计算的交点(若(i,j)mx则减 1)”。若该值等于cnt_mx,说明第i行和第j列能覆盖所有mx,标记flag=1

  4. 输出结果:若flag=1(存在有效行和列),则结果为mx - 1;否则为mx

  
  #include<iostream>
  #include<vector>
  using namespace std;
  int main() {
    int t;
    cin >> t;
    while (t--) {
      int n, m;
      cin >> n >> m;
      vector<vector<int>> a(n, vector<int> (m));
      int mx = 0, cnt_mx = 0;
      //记录最大值及其出现次数
      for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
          cin >> a[i][j];
          if (a[i][j] > mx) mx = a[i][j], cnt_mx = 1;
          else if (a[i][j] == mx) cnt_mx++;
        }
      }
      vector<int> r(n), c(m);
      for (int i = 0; i < n; i++) {// 存每行、每列中最大值的数量
        for (int j = 0; j < m; j++) {
          if (a[i][j] == mx) {
            r[i]++;
            c[j]++;
          }
        }
      }
      //检查是否存在一个位置,能覆盖所有mx
      int flag = 0;
      for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
       //行i的mx数量+列j的mx数量-当前位置是否是mx==总mx数量
          if (r[i] + c[j] - (a[i][j] == mx) == cnt_mx) {
            flag = 1;
            break;
          }
        }
        if(flag) break;
      }
      cout << mx - flag << endl;
    }
    return 0;
  }

E - 你有什么问题?

要解决这个问题,我们需要找到在区间[l, r]内的整数x,使得f(l, x) + f(x, r)的值最小,其中f(a, b)定义为两个数的十进制表示中对应位置数字相同的位数。

  1. 函数f(a, b)的性质f(a, b)ab对应位数字相同的数量。例如f(123, 153) = 2(百位和个位相同)。因此,f(l, x) + f(x, r)的含义是:xl相同的位数,加上xr相同的位数,我们需要最小化这个总和。

  2. 关键观察

    • lr的最长公共前缀为p(即前p位数字完全相同,第p位不同),则x的前p位可以与lr保持一致(这样f(l, x)f(x, r)在前p位各贡献p,总和为2p)。

    • p位是lr的差异点,这一位的处理决定了总和的最小值。

  3. 分情况讨论

    情况 1:l == r此时x只能是l(或r),f(l, x)f(x, r)均等于字符串长度(所有位都相同),因此总和为2 * 长度

    情况 2:l != r先计算最长公共前缀长度p(前p位相同,第p位不同)。

    • l[p] + 1 < r[p](即第p位存在中间数字,例如l[p] = '3'r[p] = '5',中间有'4'):此时x的第p位可以取中间值(如'4'),使得x的第p位与lr均不同(f(l, x)f(x, r)在第p位贡献0)。后面的位可以任意取值(只要x[l, r]内),且与lr均不同,因此总和为2p(仅前p位贡献)。

    • l[p] + 1 >= r[p](即第p位相差1,无中间数字,例如l[p] = '3'r[p] = '4'):此时x的第p位只能是l[p]r[p](否则x不在[l, r]内)。无论取哪一个,第p位的总和贡献为1(例如x[p] = l[p]时,f(l, x)1f(x, r)0,总和1)。因此基础总和为2p + 1。此外,若后面的位i满足l[i] = '9'r[i] = '0'(因进位导致,例如l = "199"r = "200"),x的第i位只能是90,每一位贡献1,因此总和需加1,直到遇到不满足该条件的位为止。

  
  #include<iostream>//电子的问题
  #include<string>
  using namespace std;
  string l, r;
  int main() {
    int t;
    cin >> t;
    while (t--) {
      cin >> l >> r;
      if (l == r) {
        cout << 2 * l.size() << endl;
        continue;
      }
      int p = 0;
      while (p < l.size() && l[p] == r[p]) p++; //最长公共前缀的长度p
      if (l[p] + 1 < r[p]) cout << 2 * p << endl; //存在中间数字
      else { //无中间数字,相差 1
        int rs = 2 * p + 1;
        for (int i = p + 1; i < l.size(); i++) {
          if (l[i] == '9' && r[i] == '0') rs++;
          else break;
        }
        cout << rs << endl;
      }
    }
  ​
  }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值