蓝桥杯真题(更新至2023年) - 编程题库 - C语言网 (dotcpp.com)
A日期统计
小蓝现在有一个长度为 100 的数组,数组中的每个元素的值都在 0 到 9 的范围之内。
数组中的元素从左至右如下所示:
5 6 8 6 9 1 6 1 2 4 9 1 9 8 2 3 6 4 7 7 5 9 5 0 3 8 7 5 8 1 5 8 6 1 8 3 0 3 7 9 2 7 0 5 8 8 5 7 0 9 9 1 9 4 4 6 8 6 3 3 8 5 1 6 3 4 6 7 0 7 8 2 7 6 8 9 5 6 5 6 1 4 0 1 0 0 9 4 8 0 9 1 2 8 5 0 2 5 3 3
现在他想要从这个数组中寻找一些满足以下条件的子序列:
1. 子序列的长度为 8;
2. 这个子序列可以按照下标顺序组成一个 yyyymmdd 格式的日期,并且
要求这个日期是 2023 年中的某一天的日期,例如 20230902,20231223。
yyyy 表示年份,mm 表示月份,dd 表示天数,当月份或者天数的长度只有一位时需要一个前导零补充。
请你帮小蓝计算下按上述条件一共能找到多少个不同的 2023 年的日期。
对于相同的日期你只需要统计一次即可。
本题的结果为一个整数,在提交答案时只输出这个整数,输出多余的内容将无法得分。
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<map>
#include<algorithm>
#define maxn 200
#define lli long long int
using namespace std;
lli a[101];
map<int, int>mp;
int check(int data)//这个函数感觉不用讲
{
if (mp[data] == 1)//判重
return 0;
mp[data] = 1;
int y = data / 10000;
int m = data / 100 % 100;
int d = data % 100;
if (m <= 0 || m > 12)
return 0;
if (m == 1 || m == 3 || m == 5 || m == 7 || m == 8 || m == 10 || m == 12)
{
if (d > 31 || d == 0)
return 0;
}
else if (m == 2)
{
if (d > 28 || d == 0)
return 0;
}
else
{
if (d > 30 || d == 0)
return 0;
}
return 1;
}
int ans = 0;
int nnn[1000];
int cnt = 0;
void dfs(int data, int num, int pos)
{
if (num == 100)
return;
if (pos == 8)
{
if (check(data))
{
ans++;
//nnn[++cnt] = data;
}
return;
}
if (pos == 0 && a[num] == 2 ||
pos == 1 && a[num] == 0 ||
pos == 2 && a[num] == 2 ||
pos == 3 && a[num] == 3 ||
pos == 4 && a[num] <= 1 && a[num] >= 0 ||
pos == 5 && a[num] >= 0 && a[num] <= 9 ||
pos == 6 && a[num] >= 0 && a[num] <= 3 ||
pos == 7 && a[num] >= 0 && a[num] <= 9
)//这里粗略判断,check仔细判断
//如果符合这个条件,就可以在这个基础从后面找下一位
dfs(data * 10 + a[num], num + 1, pos + 1);
dfs(data, num + 1, pos);//这个a[num]不能当pos位置的数字,就无视他看他下一个行不行。
}
void solve()
{
int n, m;
for (int i = 0; i < 100; i++)
cin >> a[i];
dfs(0, 0, 0);
cout << ans;
/*sort(nnn + 1, nnn + 1 + cnt);
for (int i = 1; i <= ans; i++)
cout << nnn[i] << '\n';*/
//这里是我看看所有日期是不是都符合条件
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
//cin >> t;
while (t--)
solve();
}
B 01熵
对于一个长度为 n 的 01 串 S = x1x2x3...xn.
香农信息熵的定义为:
。
其中 p(0), p(1) 表示在这个 01 串中 0 和 1 出现的占比。
比如,对于S = 100 来说,信息熵 H(S ) = - 1/3 log2(1/3) - 2/3 log2(2/3) - 2/3 log2(2/3) = 1.3083。
对于一个长度为23333333 的 01 串,如果其信息熵为 11625907.5798,且 0 出现次数比 1 少,那么这个01 串中 0 出现了多少次?
本题的结果为一个整数,在提交答案时只输出这个整数,输出多余的内容将无法得分。
思路:这个照着题意敲就行,纯暴力。
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cmath>
#include<map>
#include<algorithm>
#define maxn 200
#define lli long long int
using namespace std;
lli a[101];
map<int, int>mp;
void solve()
{
double n= 23333333;
double h= 11625907.5798;
for (int i = 1; i < n / 2; i++)
{
double ans = -1.0*i* (i / n) * log2(i / n) - 1.0*(n-i)*((n - i) / n) * log2((n - i) / n);
if (ans > 11625907.57&&ans< 11625907.58)
{
cout << i;
return ;
}
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
//cin >> t;
while (t--)
solve();
}
C冶炼金属
思路:这题我写了几个数,发现就是取一堆数据的最小值的最大值,和最大值的最小值(人话:公共部分)。看样例的解释,凑出的通式。
#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<map>
#include<stack>
#include<set>
#include<queue>
#define maxn 500010
//#define ll long long
#define int long long
int n;
int a, b;
int minn = 0, maxx = 0x3f3f3f3f3f;
int x, y;
using namespace std;
void solve()
{
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a >> b;
x = a * 1.0 / (b + 1)+1;
y = a * 1.0 / b;
minn = max(x, minn);
maxx = min(y, maxx);
}
cout <<minn<<" "<< maxx;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
//int t;
//cin >> t;
//while(t--)
solve();
return 0;
}
二分:
#include<iostream>
#define maxn 100010
using namespace std;
int a[maxn], b[maxn];
int t;
int c1(int x)
{
for (int i = 1; i <= t; i++)
if (a[i] / x > b[i])//超出了最小值
return 0;
return 1;
}
int c2(int x)
{
for (int i = 1; i <= t; i++)
if (a[i] / x < b[i])//取大了
return 0;
return 1;
}
int main()
{
int maxx=0, minn = 1e9 + 10;
cin >> t;
int l = 1, r = t,mid=0;
for (int i = 1; i <= t; i++)
{
cin >> a[i] >> b[i];
r = max(r, a[i]);
}
int mm = r;
while (l <= r)
{
mid = (l + r) / 2;
if (c1(mid))
r= mid-1;
else//x取小了,应该往大了取
l = mid+1;
}
minn = l;
l = 1, r = mm, mid = 0;
while (l <= r)
{
mid = (l + r) / 2;
if (c2(mid))
l = mid + 1;
else//x取大了,应该往小了取
r = mid-1;
}
maxx =l;
cout << minn << ' ' << maxx;
}
D飞机降落
思路:看注释即可
#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<map>
#include<stack>
#include<set>
#include<queue>
#define maxn 20
#define ll long long
#define int long long
int n, t[maxn], d[maxn], l[maxn],flag,f[maxn];
//n:飞机数;
//t[]:到达时间
//d[]:盘旋时间
//l[]:落地所需时间
//flag:是否能全都塞下
//f[]:是否来过这个
using namespace std;
void dfs(int x,int tim)
{
if (flag )
return;
if (x == n)
{
flag = 1;
return;
}
for (int i = 1; i <= n; i++)
{
//暴力深搜,只要当前时间小于当前飞机最晚降落时间就进入判断
if (f[i] == 0 && tim <= t[i] + d[i])
{
f[i] = 1;//来过
//dfs(已经安排下的飞机的个数,此飞机到地面的时间)
dfs(x + 1, max(t[i], tim) + l[i]);
//t[i]是到达时间,tim则是当前时间可能会让飞机盘旋一会儿
if (flag)
return;
f[i] = 0;//防止影响其他路径的标记
}
}
}
void solve()
{
memset(t, 0, sizeof(t));
memset(d, 0, sizeof(d));
memset(l, 0, sizeof(l));
memset(f, 0, sizeof(f));
flag = 0;
cin >> n;
for(int i=1;i<=n;i++)
cin >> t[i] >> d[i] >> l[i];
dfs(0, 0);
if (flag)
cout << "YES" << endl;
else
cout << "NO" << endl;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--)
solve();
return 0;
}
E: 接龙数列
思路:在注释里
#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<map>
#include<stack>
#include<set>
#include<queue>
#define maxn 20
#define ll long long
#define int long long
int dp[10];
//也就10种可能结尾0~9
//dp[i]为以i为结尾的最长长度。
using namespace std;
void solve()
{
int n;
cin >> n;
string x;
for (int i = 1; i <= n; i++)
{
cin >> x;
int a = x[0] - '0', b = x.back() - '0';
//dp[b]是以b结尾当前最长的接龙序列,
//当前要最长,就要把上一个以a为结尾的序列加上本个的长度,和以b为结尾的序列比较。
dp[b] = max(dp[b], dp[a] + 1);
}
int maxx=0;
//看看哪个结尾的序列最长
for (int i = 0; i < 10; i++)
{
maxx = max(dp[i], maxx);
}
cout << n-maxx << endl;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
/*int t;
cin >> t;
while(t--)*/
solve();
return 0;
}
F 岛屿个数
思路:一开始我想错了,把子岛也求了。这题两遍搜索就能做,我习惯深搜。第一遍把岛屿外面的0全改成2(8方搜索),然后把里面的0改成1,第二for循环搜索遍历岛屿(四方搜索),看看有几个。
为什么第一遍8方,第二遍四方呢。首先我们要知道只有有公共边才算同一个岛屿,举个例子:
0 2 1 0 0 1 1 0
0 1 3 0 0 1 0 0
图一 图二
假设一下,岛屿外是海,岛屿内是河。图中是有两个岛的,图1的3点是海,2点也是,2点8方可以搜到3,他们也算是联通的;同理,图二只有一个岛。
#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<map>
#include<stack>
#include<set>
#include<queue>
#define maxn 20
#define ll long long
//#define int long long
int n, m;
int k = 0;
char g[55][55];
int d4[4][2] = { {0,1},{0,-1},{-1,0},{1,0} };
int d8[8][2] = { {0,1},{0,-1},{-1,0},{1,0},{-1,1},{-1,-1},{1,-1},{1,1} };
//可以深搜一条路径,每一步+1(即求长度),但是注意要从2开始,最后看有几个2
//也可以一条路径都是一个数,看看图中最大数是几
using namespace std;
void dfs1(int x, int y)
{
g[x][y] = '2';
for (int p = 0; p < 8; p++)
{
int dx = x + d8[p][0], dy = y + d8[p][1];
if (dx >= 0 && dx <= m+1 && dy >= 0 && dy <= n+1 && g[dx][dy] == '0')//
dfs1(dx, dy);
}
return;
}
void dfs2(int x, int y)
{
g[x][y] = '0';//这里只要不是1就行,随便改
for (int p = 0; p < 4; p++)
{
int dx = x + d4[p][0], dy = y + d4[p][1];
if (dx >= 1 && dx <= m && dy >= 1 && dy <= n && g[dx][dy] == '1')
dfs2(dx, dy);
}
return;
}
void solve()
{
k = 0;
memset(g,'0', sizeof(g));
cin >> m>>n;
for(int i=1;i<=m;i++)
for (int j = 1; j <= n; j++)
cin >> g[i][j];
dfs1(0,0);
for (int i = 0; i <= m+1; i++)
for (int j = 0; j <= n+1; j++)//这里是为了保证所有岛屿外面有水,并且通过dfs让水2灌满所有没岛的地方
if (g[i][j] == '0')
g[i][j] = '1';
//cout << endl;
//for (int i = 0; i <= m+1; i++)
//{
// for (int j = 0; j <= n+1; j++)
// cout << g[i][j];
// cout << endl;
//}
for(int i=0;i<=m+1;i++)
for (int j = 0; j <= n+1; j++)
{
if (g[i][j] == '1')
{
dfs2(i,j);
k++;
}
}
//cout << endl;
cout << k << endl;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--)
solve();
return 0;
}
G子串个数
思路:找到所有头l和尾r出现的位置,然后存起来。利用r[j] >= l[i] + k - 1这一关系,找出每一个头的总数,加和即可。
#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<map>
#include<stack>
#include<set>
#include<queue>
#define maxn 500010
//#define ll long long
#define int long long
using namespace std;
void solve()
{
int k;
cin >> k;
string s;
char a, b;
cin >> s>>a>>b;
//cout << s <<endl<<a <<endl<< b;
int n = s.size();
int l[maxn], r[maxn];
int ll = 0, rr = 0;
for (int i = 0; i < n; i++)
{
if (s[i] == a)
l[++ll] = i;//开头的下标
if (s[i] == b)
r[++rr] = i;//结尾的下标
}
//以每一个开头的下标为基,看看右边几个比他大的
int ans = 0;
//for (int i = 1; i <= ll; i++)
//{
// for (int j = rr; j >= 1; j--)
// if (r[j] < l[i]+k-1)//第j个右点小于基点,也就是从这开始不能形成字符串
// {
// ans += rr - j;//减去即可
// break;
// }
//}
//上述暴力仅能95分,我们再优化一下。我们可以注意到前面很多点其实是无效重复跑的。
//比如说ababb,k=2.第一个b肯定是无效的,但是我们还在跑;第二个b生效一次之后,
//只有后面的b有效,所以我们可以找个数存一下,以后查询直接跳过去。
int temp = 1;
for (int i = 1; i <= ll; i++)
{
for (int j = temp; j <= rr; j++)
{
//倒着跑是找第一个小于的,正着跑是找第一个大于的再-1。
if (r[j] >= l[i] + k - 1)//第j个右点小于基点,也就是从这开始不能形成字符串
{
temp = j;
ans += rr - j+1;//减去即可
//这里的+1大家可能没明白,就比如说第j个开始就符合条件,那我们就用"总数-(j-1)"对吧,也就是这里的-j+1
break;
}
}
}
cout << ans;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
//int t;
//cin >> t;
//while(t--)
solve();
return 0;
}