cpp入门题

3 阅读16分钟

B4026 [语言月赛 202409] 灵感

这天,迅风在欣赏某地的美景时,灵感大作,在上午及下午分别写下了两篇文章,而且迅风很喜欢数文章的字数。

具体地,如果迅风在下午写下的文章的字数之和严格大于他在上午写下的文章的字数之和,则认定他的灵感随着时间的推移越积越多。

现在给出迅风所写的四篇文章的字数,请你回答他的灵感是否随着时间的推移越积越多。

输入格式

共一行,包含四个正整数 a,b,c,da,b,c,d 分别表示迅风在上午写下的两篇文章的字数及他在下午写下的两篇文章的字数。

输出格式

答案共一行,若迅风的灵感随着时间的推移越积越多,则输出 Yes;否则,输出 No

输入输出样例 #1

输入 #1

3 2 4 5

输出 #1

Yes

输入输出样例 #2

输入 #2

3 3 4 2

输出 #2

No

说明/提示

样例解释 1

第一个样例中,迅风在上午写下的文章的字数之和为 3+2=53+2=5,在下午写下的文章的字数之和为 4+5=94+5=9,由于 9>59>5,所以认定他的灵感随着时间的推移越积越多。

样例解释 2

第二个样例中,迅风在上午写下的文章的字数之和为 3+3=63+3=6,在下午写下的文章的字数之和为 4+2=64+2=6,由于 6=66=6,所以认定他的灵感不会随着时间的推移越积越多。

数据范围

对于 100%100\% 的数据,1a,b,c,d1001\le a,b,c,d\le100

#include <iostream>
using namespace std;
int main() {
  int a, b, c, d;
  cin >> a >> b >> c >> d;
  if (a + b < c + d)
    cout << "Yes";
  else
    cout << "No";
  return 0;
}

B4027 [语言月赛 202409] 重聚

小紫和小蓝是一对双胞胎,但是在游乐场里走散了。然而她们有超能力。

当她们分开一段时间后,可以感应对方的位置,让二人重聚——然而如果距离太远,那么感应也无可奈何。

具体地:

  • 小紫在分离时间 t1\ge t_1 分钟时开启感应,如果她和小蓝距离不超过 d1d_1,那么可以感应到小蓝的位置。
  • 小蓝在分离时间 t2\ge t_2 分钟时开启感应,如果她和小紫距离不超过 d2d_2,那么可以感应到小紫的位置。

当双胞胎的一个人能感应到另一个人的位置,就可以行动使得两人重聚。

现在小紫和小蓝已经分离了 tt 分钟,当前距离为 dd。她们都在原地等候。

请判断至少还需要几分钟,才能让双胞胎中的一个人感应到另一个人的位置?

输入格式

输入共有一行六个正整数 t,d,t1,d1,t2,d2t,d,t_1,d_1,t_2,d_2,含义如题目描述所示。

输出格式

输出一行一个整数,表示至少还需要等几分钟。特别地,如果无论等待多久都无法感应到,输出 1-1

输入输出样例 #1

输入 #1

7 2 10 8 12 15

输出 #1

3

输入输出样例 #2

输入 #2

11 8 12 19 10 8

输出 #2

0

输入输出样例 #3

输入 #3

100 100 10 7 12 99

输出 #3

-1

说明/提示

【样例 1 解释】

小紫在等待至少 1010 分钟后,能在距离不超过 88 时感应到小蓝的位置。

小蓝在等待至少 1212 分钟后,能在距离不超过 1515 时感应到小紫的位置。

目前已经等待 77 分钟,且两人距离为 22。再等待 33 分钟,就能让小紫开启感应,并感应到小蓝了。

【样例 2 解释】

目前已经等待了 1111 分钟,小蓝在 11 分钟前已经开启感应,并感应到小紫了,不需要额外等待。

【样例 3 解释】

无论双胞胎等多久,感应范围都达不到 100100,所以永远无法感应到对方。

【数据范围】

对于全体数据,保证 1t,d,t1,d1,t2,d21001\le t,d,t_1,d_1,t_2,d_2 \le 100

#include <iostream>
using namespace std;
int main() {
  int t, d, t1, d1, t2, d2;
  cin >> t >> d >> t1 >> d1 >> t2 >> d2;
  if (d > d1 && d > d2) {
    cout << "-1";
    return 0;
  }
  int r1 = -1, r2 = -1;
  if (d <= d1)
    r1 = max(0, t1 - t);
  if (d <= d2)
    r2 = max(0, t2 - t);
  if (r1 == -1)
    cout << r2;
  else if (r2 == -1)
    cout << r1;
  else
    cout << min(r1, r2);
  return 0;
}

B3882 [信息与未来 2015] 求回文数

一个正整数,正读和反读都相同的数为回文数,例如 22,131,2442,37073,6,22,131,2442,37073,6,\cdots。所有的 11 位数都是回文数。

现给出一个正整数 nn,求出 [1,n][1,n] 中的回文数的个数。

输入格式

一个整数 nn

输出格式

一个整数,即 1n1\sim n 中全部回文数的个数。

输入输出样例 #1

输入 #1

24

输出 #1

11

说明/提示

样例解释

112424 中,回文数有 19,11,221\sim 9,11,22,共 1111 个。

数据范围

1n1041\le n\le10^4

#include <iostream>
using namespace std;

int main() {
  int n;
  cin >> n;  // 读取输入的上限 n
  
  int cnt = 0;  // 计数器,记录回文数的个数
  
  // 遍历从 1 到 n 的所有数字
  for (int i = 1; i <= n; i++) {
    int reversed = 0;
    
    // 通过循环反转数字
    // 例如:对于数字 123,反转过程:
    // 第1次:reversed = 0×10 + 3 = 3, n2 = 12
    // 第2次:reversed = 3×10 + 2 = 32, n2 = 1  
    // 第3次:reversed = 32×10 + 1 = 321, n2 = 0
    for (int n2 = i; n2; n2 /= 10)
      reversed = reversed * 10 + (n2 % 10);
    
    // 判断是否为回文数
    if (reversed == i)
      cnt++;  // 如果是回文数,计数器加1
  }
  
  cout << cnt;  // 输出回文数的总个数
  return 0;
}

P1428 小鱼比可爱

人比人,气死人;鱼比鱼,难死鱼。小鱼最近参加了一个“比可爱”比赛,比的是每只鱼的可爱程度。参赛的鱼被从左到右排成一排,头都朝向左边,然后每只鱼会得到一个整数数值,表示这只鱼的可爱程度,很显然整数越大,表示这只鱼越可爱,而且任意两只鱼的可爱程度可能一样。由于所有的鱼头都朝向左边,所以每只鱼只能看见在它左边的鱼的可爱程度,它们心里都在计算,在自己的眼力范围内有多少只鱼不如自己可爱呢。请你帮这些可爱但是鱼脑不够用的小鱼们计算一下。

输入格式

第一行输入一个正整数 nn,表示鱼的数目。

第二行内输入 nn 个正整数,用空格间隔,依次表示从左到右每只小鱼的可爱程度 aia_i

输出格式

一行,输出 nn 个整数,用空格间隔,依次表示每只小鱼眼中有多少只鱼不如自己可爱。

输入输出样例 #1

输入 #1

6
4 3 0 5 1 2

输出 #1

0 0 0 3 1 2

说明/提示

对于 100%100\% 的数据,1n1001 \leq n\leq 1000ai100 \leq a_i \leq 10

#include <iostream>
using namespace std;

int main() {
  int n;  // 小鱼的数量
  cin >> n;  // 读取小鱼数量
  
  int fishes[102];  // 存储每只小鱼的可爱程度,数组大小设为102(n≤100,留有余量)
  
  // 遍历每只小鱼
  for (int i = 0; i < n; i++) {
    cin >> fishes[i];  // 读取当前小鱼的可爱程度
    int thisOne = fishes[i];  // 当前小鱼的可爱程度值
    int cnt = 0;  // 计数器,记录左边不如当前小鱼可爱的数量
    
    // 遍历当前小鱼左边的所有小鱼
    // j 从 0 到 i-1,表示当前小鱼能看到的所有左边小鱼
    for (int j = 0; j < i; j++) {
      // 如果左边的小鱼可爱程度小于当前小鱼
      if (fishes[j] < thisOne)
        cnt++;  // 计数器加1
    }
    
    // 输出当前小鱼的结果
    cout << cnt << " ";  // 输出统计结果,后面加空格分隔
  }
  
  return 0;
}

P1152 欢乐的跳

一个 nn 个元素的整数数组,如果数组两个连续元素之间差的绝对值包括了 [1,n1][1,n-1] 之间的所有整数,则称之符合“欢乐的跳”,如数组 {1,4,2,3}\{1,4,2,3\} 符合“欢乐的跳”,因为差的绝对值分别为:3,2,13,2,1

给定一个数组,你的任务是判断该数组是否符合“欢乐的跳”。

输入格式

每组测试数据第一行以一个整数 n(1n1000)n(1 \le n \le 1000) 开始,接下来 nn 个空格隔开的在 [108,108][-10^8,10^8] 之间的整数。

输出格式

对于每组测试数据,输出一行若该数组符合“欢乐的跳”则输出 Jolly,否则输出 Not jolly

输入输出样例 #1

输入 #1

4 1 4 2 3

输出 #1

Jolly

输入输出样例 #2

输入 #2

5 1 4 2 -1 6

输出 #2

Not jolly

说明/提示

1n10001 \le n \le 1000

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

int main() {
  int n;  // 数组元素个数
  cin >> n;
  
  // 布尔数组,用于标记差值是否出现,初始化为false
  // 数组大小1006是为了防止越界(n≤1000)
  bool b[1006] = {0};
  
  long long int last;  // 存储上一个元素的值
  cin >> last;  // 读取第一个元素
  
  // 遍历后续的 n-1 个元素
  for (int i = 1; i < n; i++) {
    long long int k;  // 当前元素
    cin >> k;
    
    // 计算当前元素与上一个元素的差值绝对值
    long long int d = abs(k - last);
    last = k;  // 更新last为当前元素,用于下一次计算
    
    // 如果差值在合理范围内(1到1000),标记为已出现
    // 这个检查是为了防止数组越界
    if (d < 1001)
      b[d] = true;
  }
  
  // 检查从1到n-1的所有差值是否都出现了
  for (int i = 1; i < n; i++) {
    // 如果某个差值没有出现
    if (!b[i]) {
      cout << "Not jolly";
      return 0;
    }
  }
  
  // 所有要求的差值都出现了,输出Jolly
  cout << "Jolly";
  return 0;
}

P1116 车厢重组

在一个旧式的火车站旁边有一座桥,其桥面可以绕河中心的桥墩水平旋转。一个车站的职工发现桥的长度最多能容纳两节车厢,如果将桥旋转 180180 度,则可以把相邻两节车厢的位置交换,用这种方法可以重新排列车厢的顺序。于是他就负责用这座桥将进站的车厢按车厢号从小到大排列。他退休后,火车站决定将这一工作自动化,其中一项重要的工作是编一个程序,输入初始的车厢顺序,计算最少用多少步就能将车厢排序。

输入格式

共两行。

第一行是车厢总数 N(10000)N( \le 10000)

第二行是 NN 个不同的数表示初始的车厢顺序。
:实际上数据中并不都在同一行,有可能分行输入)

输出格式

一个整数,最少的旋转次数。

输入输出样例 #1

输入 #1

4
4 3 2 1

输出 #1

6
#include <algorithm>
#include <iostream>
#include <iterator>
#include <utility>
#include <vector>
using namespace std;

// 使用冒泡排序计算交换次数
int sort(vector<int> &a) {
  int len = a.size();  // 获取数组长度
  int cnt = 0;         // 计数器,记录交换次数
  
  // 外层循环:进行 n-1 轮排序
  // 每轮排序会将当前最大元素"冒泡"到正确位置
  while (len--) {
    // 内层循环:遍历未排序的部分
    // 比较相邻元素,如果逆序就交换
    for (int j = 1; j <= len; j++) {
      // 如果前一个元素大于后一个元素,说明需要交换
      if (a[j - 1] > a[j]) {
        swap(a[j - 1], a[j]);  // 交换两个元素
        cnt++;                 // 交换次数加1
      }
    }
    // 每轮结束后,当前最大的元素已经移动到正确位置
    // len 减小,下一轮不需要再考虑已排序的部分
  }
  return cnt;  // 返回总交换次数
}

int main() {
  int n;  // 车厢总数
  cin >> n;
  
  vector<int> trains(n);  // 创建存储车厢顺序的向量
  
  // 读取初始的车厢顺序
  for (int i = 0; i < n; i++) {
    cin >> trains[i];
  }
  
  // 调用排序函数并输出交换次数
  cout << sort(trains);
  
  return 0;
}

为何问题可转化为冒泡排序问题

  • 它只进行相邻交换

  • 每次交换都消除一个逆序对

  • 不会进行不必要的交换

为什么这是最优的?

在一个长度为 nn 的序列 A=[a1,a2,,an]A=[a_1,a_2,\dots ,a_n] 中,若下标满足 i<ji<jai>aja_i>a_j,则称 (i,j)(i,j) 为一个逆序对。序列的逆序对总数记为 Inv(A)\text{Inv}(A)。当序列已升序时,Inv=0\text{Inv}=0

  1. 每次交换只会消除恰好一个逆序对
  • ak>ak+1a_k>a_{k+1}(即这是一对逆序),交换后 (k,k+1)(k,k+1) 不再是逆序对,逆序对数 减 1。
  • 对于其他受影响的对:
    • 对于任意 i<ki<k,原来 (i,k)(i,k)(i,k+1)(i,k+1) 的大小关系在交换后保持不变,因为两者只涉及同一组数的相对位置。
    • 对于任意 j>k+1j>k+1,同理 (k,j)(k,j)(k+1,j)(k+1,j) 的相对大小也不变。
  • 因此 没有新的逆序对产生,整体逆序对数仅减少 1。

参考《Introductory Programming in C#》“Each exchange in Bubble Sort removes precisely one inversion; therefore, Bubble Sort requires O(N²) exchanges.”

  1. 逆序对数是任何排序过程所需最少交换次数的下界

每一次相邻交换最多只能消除一个逆序对(如上所述),所以把所有逆序对全部消除所需的交换次数不可能少于Inv(A)\text{Inv}(A)

于是得到下界: 最少交换次数Inv(A)\text{最少交换次数} \ge \text{Inv}(A)

  1. 冒泡排序恰好达到该下界 → 最优
    • 只在出现逆序 (aj>aj+1)(a_j>a_{j+1}) 时交换。
    • 如第 1 步所证,每次交换必然把逆序对数减 1。
    • 经过全部循环后,逆序对数从初始的 Inv(A)\text{Inv}(A) 逐步降至 0,恰好用了 Inv(A)\text{Inv}(A) 次交换。

P1125 [NOIP 2008 提高组] 笨小猴

笨小猴的词汇量很小,所以每次做英语选择题的时候都很头疼。但是他找到了一种方法,经试验证明,用这种方法去选择选项的时候选对的几率非常大!

这种方法的具体描述如下:假设 maxn\text{maxn} 是单词中出现次数最多的字母的出现次数,minn\text{minn} 是单词中出现次数最少的字母的出现次数,如果 maxnminn\text{maxn}-\text{minn} 是一个质数,那么笨小猴就认为这是个 Lucky Word,这样的单词很可能就是正确的答案。

输入格式

一个单词,其中只可能出现小写字母,并且长度小于 100100

输出格式

共两行,第一行是一个字符串,假设输入的单词是 Lucky Word,那么输出 Lucky Word,否则输出 No Answer

第二行是一个整数,如果输入的单词是 Lucky Word,输出 maxnminn\text{maxn}-\text{minn} 的值,否则输出 00

输入输出样例 #1

输入 #1

error

输出 #1

Lucky Word
2

输入输出样例 #2

输入 #2

olympic

输出 #2

No Answer
0

说明/提示

【输入输出样例 1 解释】

单词 error 中出现最多的字母 r\texttt r 出现了 33 次,出现次数最少的字母出现了 11 次,31=23-1=222 是质数。

【输入输出样例 2 解释】

单词 olympic 中出现最多的字母 i\texttt i 出现了 11 次,出现次数最少的字母出现了 11 次,11=01-1=000 不是质数。

(本处原题面错误已经修正)

noip2008 提高第一题

#include <cstdio>
#include <cstring>
#define pCount 202

using namespace std;
int main() {
  bool notPrime[pCount] = {1, 1, 0};
  for (int i = 2; i < pCount; i++) {
    if (!notPrime[i]) {
      for (int j = i + i; j < pCount; j += i) {
        notPrime[j] = 1; // 标记i的所有倍数为非质数
      }
    }
  }
  int l[26] = {0};
  char word[102];
  scanf("%s", word);
  int len = strlen(word);
  if (len < 2) {
    printf("No Answer\n0");
  }
  for (int i = 0; i < len; i++) {
    l[word[i] - 'a']++; // 对应字母计数加1
  }
  int max = 0, min = 1000;
  for (int i = 0; i < 26; i++) {
    if (l[i] == 0)
      continue; // 跳过未出现的字母
    if (l[i] > max)
      max = l[i];
    if (l[i] < min)
      min = l[i];
  }
  if (!notPrime[max - min])
    printf("Lucky Word\n%d", max - min);
  else
    printf("No Answer\n0");
}

补充:埃筛法原理

核心思想:素数的倍数都不是素数

素数筛,是数论的基础,故其自然能预处理出一些数论函数

基于筛法本身的思想,进行拓展,如:

  • 预处理每个数的最大质因子 埃筛, d[x]d[x]记录xx是上一次是被哪个质数筛掉的,显然最后一次记录就是其最大质因子。
  • xx的素因子表 埃筛,每次xx被筛时,将质数加入动态数组中

B3640 T3 句子反转

请尽量在 30min之内写完题目。这是指「写代码」的时间;「读题」时间不计算在内。

给定一行句子,每个词之间用空格隔开,要么是全小写英文单词,要么是全大写英文单词,要么是自然数。

要求将这些单词倒序输出。而且对于每个单词,如果是小写词,应当转为大写;如果是大写词,应当转为小写;如果是自然数,应该倒转输出。

举一个例子:

we choose TO go 2 the 123 moon

程序应当输出:

MOON 321 THE 2 GO to CHOOSE WE

输入格式

仅一行,即需要反转的句子。

输出格式

仅一行,表示程序对句子的处理结果。

输入输出样例 #1

输入 #1

we choose TO go 2 the 123 moon

输出 #1

MOON 321 THE 2 GO to CHOOSE WE

说明/提示

样例解释

首先应当按单词逆序,即:

moon 123 the 2 go TO choose we

小写变大写、大写变小写、倒转自然数之后,得到最终结果:

MOON 321 THE 2 GO to CHOOSE WE

数据规模与约定

对于 100%100\% 的数据,句子中包含的单词数量不超过 10001000,每个单词长度不超过 66

#include <stdio.h>
#include <string.h>

int main() {
  // 定义二维数组存储单词,最多1000个单词,每个单词长度不超过6
  // 额外+1是为了存储字符串结束符'\0'
  char s[1002][7] = {0};
  int len = 0;  // 记录实际读取的单词数量
  
  int loop = 1;  // 循环控制标志
  while (loop) {
    char *w = s[len++];  // 获取当前单词的指针,同时len自增
    
    // 读取一个单词的每个字符
    for (int i = 0; i < 7; i++) {
      char c = getchar();  // 读取一个字符
      
      if (c == ' ') {
        break;  // 遇到空格,当前单词读取结束
      }
      
      if (c == EOF || c == '\n') {
        loop = 0;  // 遇到文件结束或换行符,整个输入结束
        break;
      }
      
      w[i] = c;  // 将字符存储到当前单词中
    }
  }
  
  // 逆序处理所有单词,从最后一个单词开始到第一个单词
  for (int i = len; i--;) {
    char *w = s[i];  // 获取当前要处理的单词
    
    // 判断单词类型并相应处理:
    
    // 情况1:如果是数字(自然数)
    if (w[0] >= '0' && w[0] <= '9') {
      // 将数字字符串倒序输出
      for (int i = strlen(w); i--;) {
        putchar(w[i]);
      }
    } 
    // 情况2:如果是小写字母单词
    else if (w[0] >= 'a' && w[0] <= 'z') {
      // 将小写字母转换为大写输出
      for (int i = 0, __len = strlen(w); i < __len; i++) {
        putchar(w[i] - 'a' + 'A');  // 小写转大写:'a'-'A'=32
      }
    } 
    // 情况3:如果是大写字母单词
    else {
      // 将大写字母转换为小写输出
      for (int i = 0, __len = strlen(w); i < __len; i++) {
        putchar(w[i] - 'A' + 'a');  // 大写转小写:'A'-'a'=-32
      }
    }
    
    putchar(' ');  // 在每个单词后输出空格
  }
  
  return 0;
}