[关闭]
@guxier 2022-03-28T11:16:12.000000Z 字数 5671 阅读 361

蓝桥杯3/28

竞赛部培训


文件格式转换

原题链接

题目描述

小明正在分析一本小说中的人物相关性。他想知道在小说中 Alice 和 Bob 有多少次同时出现。

更准确的说,小明定义 Alice 和 Bob "同时出现" 的意思是:在小说文本 中 Alice 和 Bob 之间不超过 K 个字符。

例如以下文本:

This is a story about Alice and Bob.Alice wants to send a private message to Bob.

假设 K = 20,则 Alice 和 Bob 同时出现了 2 次,分别是"Alice and Bob" 和 "Bob. Alice"。前者 Alice 和 Bob 之间有 5 个字符,后者有 2 个字符。

注意:

1. Alice 和 Bob 是大小写敏感的,alice 或 bob 等并不计算在内。

2. Alice 和 Bob 应为单独的单词,前后可以有标点符号和空格,但是不能 有字母。例如 Bobbi 並不算出现了 Bob。

输入描述

第一行包含一个整数

第二行包含一行字符串,只包含大小写字母、标点符号和空格。长度不超过

输出描述

输出一个整数,表示 Alice 和 Bob 同时出现的次数。

输入输出样例

输入

  1. 20
  2. This is a story about Alice and Bob.Alice wants to send a private
  3. message to Bob.

输出

  1. 2

运行限制

最大运行时间:1s
最大运行内存: 512M

题解

string.find(str, pos)如果找到目标,则返回该子字符串首次出现时其首字符的索引;否则,返回string::npos


  1. #include <cstdio>
  2. #include <string>
  3. #include <iostream>
  4. #include <vector>
  5. #include <algorithm>
  6. typedef long long LL;
  7. using namespace std;
  8. string a = "Alice", b = "Bob";
  9. int main()
  10. {
  11. LL k;
  12. string s;
  13. cin >> k;
  14. getchar();
  15. getline(cin , s);
  16. vector<int>va,vb;
  17. int ita = 0 , itb = 0;
  18. while(s.find(a ,ita+1) != string::npos)
  19. {
  20. ita = s.find(a , ita);
  21. va.push_back(ita);
  22. ita++;
  23. }
  24. while(s.find(b , itb) != string::npos)
  25. {
  26. itb = s.find(b , itb);
  27. vb.push_back(itb);
  28. itb++;
  29. }
  30. LL l = 0 , r = 0;
  31. LL cnt = 0;
  32. for(LL i = 0 ; i<va.size() ; i++)
  33. {
  34. while(l<vb.size() && va[i]-vb[l]-3 > k) l++;
  35. while(r<vb.size() && vb[r]-va[i]-5 <= k) r++;
  36. cnt += r-l;
  37. }
  38. cout << cnt;
  39. return 0;
  40. }

包子凑数

原题链接

题目描述

资源限制

时间限制:1.0s 内存限制:256.0MB

 小明几乎每天早晨都会在一家包子铺吃早餐。他发现这家包子铺有N种蒸笼,其中第i种蒸笼恰好能放Ai个包子。每种蒸笼都有非常多笼,可以认为是无限笼。

  每当有顾客想买X个包子,卖包子的大叔就会迅速选出若干笼包子来,使得这若干笼中恰好一共有X个包子。比如一共有3种蒸笼,分别能放3、4和5个包子。当顾客想买11个包子时,大叔就会选2笼3个的再加1笼5个的(也可能选出1笼3个的再加2笼4个的)。

  当然有时包子大叔无论如何也凑不出顾客想买的数量。比如一共有3种蒸笼,分别能放4、5和6个包子。而顾客想买7个包子时,大叔就凑不出来了。

  小明想知道一共有多少种数目是包子大叔凑不出来的。

输入格式

  第一行包含一个整数N。(1 <= N <= 100)
  以下N行每行包含一个整数Ai。(1 <= Ai <= 100)

输出格式

  一个整数代表答案。如果凑不出的数目有无限多个,输出INF。

样例

Input

  1.   2
  2.   4
  3.   5

Output

  1.   6

  再例如,
Input

  1.   2
  2.   4
  3.   6

Output

  1.   INF

样例说明
  对于样例1,凑不出的数目包括:1, 2, 3, 6, 7, 11。
  对于样例2,所有奇数都凑不出来,所以有无限多个。

  资源约定:
  峰值内存消耗(含虚拟机) < 256M
  CPU消耗 < 1000ms


严格的来讲,如果将其边界定义为其所有数字的最小公倍数,在4号用例之后会超时

以4号用例
Input:

  1. 10
  2. 87
  3. 86
  4. 56
  5. 84
  6. 96
  7. 62
  8. 83
  9. 71
  10. 76
  11. 73

得出其最小公倍数:
Lan_BaoZi.jpg
因此 凑不出的数目为有限个时,找的上界可以自己定,只要不超时就行
Output

  1. 135

C++ 代码

  1. #include <cstdio>
  2. #include <cmath>
  3. #include <iostream>
  4. #include <algorithm>
  5. #define rep(i, a, n) for (int i = a; i <= n; i++)
  6. #define per(i, a, n) for (int i = n; i >= a; i--)
  7. typedef long long ll;
  8. using namespace std;
  9. const int N = 1e5;
  10. int n, a[N], dp[N];
  11. inline ll lcm(ll a,ll b)
  12. {
  13. return a * b / __gcd(a,b);
  14. }
  15. int main()
  16. {
  17. ll sum = 1;
  18. cin >> n;
  19. rep(i, 1, n)
  20. {
  21. cin >> a[i];
  22. // sum = lcm(sum, a[i]);
  23. // sum *= a[i];
  24. }
  25. int x = a[1];
  26. rep(i, 2, n)
  27. x = __gcd(x, a[i]);
  28. if(x != 1)
  29. {
  30. cout << "INF" << endl;
  31. return 0;
  32. }
  33. dp[0] = 1;
  34. rep(i , 1 , n)
  35. for(ll j = 0; j <= N; j++)
  36. {
  37. if(j < a[i]) continue;
  38. if(dp[j - a[i]]) dp[j] = 1;
  39. }
  40. int num = 0;
  41. for(ll j = 1 ; j < N; j++)
  42. if(!dp[j]) num ++;
  43. cout << num << endl;
  44. // debug(num);
  45. return 0;
  46. }

游园安排

原题链接

【问题描述】

L 星球游乐园非常有趣,吸引着各个星球的游客前来游玩。小蓝是 L 星球游乐园的管理员。为了更好的管理游乐园,游乐园要求所有的游客提前预约,小蓝能看到系统上所有预约游客的名字。每个游客的名字由一个大写英文字母开始,后面跟 0 个或多个小写英文字母。游客可能重名。小蓝特别喜欢递增的事物。今天,他决定在所有预约的游客中,选择一部分游客在上午游玩,其他的游客都在下午游玩,在上午游玩的游客要求按照预约的顺序排列后,名字是单调递增的,即排在前面的名字严格小于排在后面的名字。一个名字 A 小于另一个名字 B 是指:存在一个整数,使得 A 的前 个字母与 B 的前个字母相同,且 A 的第 个字母小于 B 的第 个字母。(如果 A 不存在第 个字母且 B 存在第 个字母,也视为 A 的第 个字母小于 B 的第 个字母)作为小蓝的助手,你要按照小蓝的想法安排游客,同时你又希望上午有尽量多的游客游玩,请告诉小蓝让哪些游客上午游玩。如果方案有多种,请输出上午游玩的第一个游客名字最小的方案。如果此时还有多种方案,请输出第一个游客名字最小的前提下第二个游客名字最小的方案。如果仍然有多种,依此类推选择第三个、第四个……游客名字最小的方案。

【输入格式】

输入包含一个字符串,按预约的顺序给出所有游客的名字,相邻的游客名字之间没有字符分隔。

【输出格式】

按预约顺序输出上午游玩的游客名单,中间不加任何分隔字符。

【样例输入】

  1. WoAiLanQiaoBei

【样例输出】

  1. AiLanQiao

【评测用例规模与约定】
对于 20% 的评测数据,输入的总长度不超过 20 个字母。
对于 50% 的评测数据,输入的总长度不超过 300 个字母。
对于 70% 的评测数据,输入的总长度不超过 10000 个字母。
对于所有评测数据,每个名字的长度不超过 10 个字母,输入的总长度不超过 1000000 个字母。


题解

题目大意
1. 一个大写单词表示一个游客,且可能重名
2. 上午的游客按照字典序排序,且人数尽可能多
3. 如果方案有多种,请输出上午游玩的游客名字的字典序最小的方案

字典序排序 :
游客a 和 游客 b

Abcd 和 Abcde ==== a < b
Abcd 和 Bbcd ===== a < b

动态规划(最长上升子序列)+递归

状态转移方程:

  1. f[i] = max(f[i] , f[j] + 1)

时间复杂度

C++ 代码

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <iostream>
  4. #include <vector>
  5. #include <algorithm>
  6. #define ios ios::sync_with_stdio(false), cin.tie(0)
  7. #define rep(i, a, n) for (register int i = a; i <= n; i++)
  8. #define per(i, a, n) for (register int i = n; i >= a; i--)
  9. #define debug(x) cout << #x << ": " << x << endl
  10. #define MOD 1000000007
  11. #define INF 0x3f3f3f3f
  12. typedef long long LL;
  13. using namespace std;
  14. const int N =1e6 + 10;
  15. string name[N]; //存储游客名字
  16. int n; //游客总数
  17. int dp[N] , m[N];
  18. // vector< vector<int> > m(N);
  19. int main()
  20. {
  21. string s;
  22. cin >> s;
  23. rep(i , 0 , s.size()-1) //记录游客名字 1~n
  24. {
  25. n++;
  26. name[n] = s[i];
  27. while(s[i+1] - 'A' > 26) name[n] += s[++i];
  28. }
  29. // debug(n); //测试输入
  30. // rep(i , 1, n)
  31. // cout << name[i] << ' ';
  32. /**
  33. * @brief
  34. * dp(i) : 以前i个人为例,上午游玩的客人方案的集合,
  35. * 属性: 人数的最大值
  36. * 对于 以第i人为结尾的所有集合,以倒数第二人区分该集合的元素
  37. * 有一个特殊点 只有一个人
  38. */
  39. rep(i , 1, n)
  40. {
  41. dp[i] = 1;
  42. m[i] = -1;
  43. rep(j , 1, i-1)
  44. if(name[j] < name[i])
  45. {
  46. if(dp[j] +1 > dp[i] ||(dp[i]==dp[j]+1 && name[j] < name[m[i]]))
  47. {
  48. dp[i] = dp[j] + 1;
  49. m[i] = j;
  50. }
  51. }
  52. }
  53. int cnt = 0 , t;
  54. rep(i , 1 , n)
  55. if(dp[i] > cnt)
  56. {
  57. cnt = dp[i];
  58. t = i;
  59. }
  60. string str, ans = name[t];
  61. rep(i ,1 , cnt-1)
  62. {
  63. str = ans;
  64. t = m[t];
  65. ans = name[t] + str;
  66. }
  67. debug(cnt);
  68. cout << ans;
  69. return 0;
  70. }

标准答案:

AtbdAvymmBhBppwCbazuDFskGjopJJeeuLegMhectMqdcyMsrqcMuNpOOneeOxPfPqnPunkdQRimpmRmowzRsrte
SzrTjTsshUr

我的输出:

AtbdAvymmBhBppwCbazuDFskGjopJJeeuLegMhectMqdcyMsrqcMuNpOOneeOxPfPqnPunkdQRimpmRmowzRsrte
TVtXifbZ

分析:

如果方案有多种输出字典序最小的方案

使用二分


  1. #include <cstdio>
  2. #include <cstring>
  3. #include <iostream>
  4. #include <vector>
  5. #include <algorithm>
  6. #define rep(i, a, n) for (register int i = a; i <= n; i++)
  7. #define per(i, a, n) for (register int i = n; i >= a; i--)
  8. #define debug(x) cout << #x << ": " << x << endl
  9. typedef long long LL;
  10. using namespace std;
  11. const int N =1e6 + 10;
  12. string name[N]; //存储游客名字
  13. int n; //游客总数
  14. string q[N];
  15. int pos[N], ans[N];
  16. int bsearch(int l ,int r, string x)
  17. {
  18. while(l < r)
  19. {
  20. int mid = l + r >> 1;
  21. if(q[mid] >= x) r = mid;
  22. else l = mid + 1;
  23. }
  24. return l;
  25. }
  26. int main()
  27. {
  28. string s;
  29. cin >> s;
  30. rep(i , 0 , s.size()-1) //记录游客名字 1~n
  31. {
  32. n++;
  33. name[n] = s[i];
  34. while(s[i+1] - 'A' > 26) name[n] += s[++i];
  35. }
  36. int len = 1;
  37. q[1] = name[1];
  38. pos[1] = len;
  39. rep(i , 2, n)
  40. {
  41. if(name[i] > q[len])
  42. {
  43. q[++len] = name[i];
  44. pos[i] = len;
  45. }
  46. else
  47. {
  48. int t = bsearch(1 , len + 1, name[i]);
  49. q[t] = name[i];
  50. pos[i] = t;
  51. }
  52. }
  53. int tmp = len;
  54. string smx = "Zzzzzzzzzz"; //10
  55. per(i , 1 , n)
  56. {
  57. if(!tmp) break;
  58. if (pos[i] == tmp && smx > name[i]) {
  59. ans[tmp] = i;
  60. tmp --;
  61. smx = name[i];
  62. }
  63. }
  64. rep(i , 1 , len)
  65. cout << name[ans[i]];
  66. return 0;
  67. }

经典最长上升子序列模板

  1. for(int i = 1 ; i <= n; i++)
  2. {
  3. dp[i]=1;
  4. for(int j = 1; j < i ;j++)
  5. if(a[j] < a[i]) dp[i] = max(dp[i] , dp[j] + 1);
  6. mx = max(mx , dp[i]);
  7. }

二分处理最长上升子序列

  1. rep(i , 1 , n)
  2. {
  3. int l = 0 , r = len;
  4. while(l < r)
  5. {
  6. int mid = l + r >> 1;
  7. if(q[mid] < name[i]) l = mid;
  8. else r = mid - 1;
  9. }
  10. len = max(len , r + 1);
  11. q[r + 1] = name[i];
  12. }

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注