T1:日期统计
解题思路
思路:2023年只有365天,枚举2023的每一天是否在数组中存在即可,时间复杂度O(365*100)
注意:还有就是不要傻傻的去复制数组加逗号了,直接输入就好了(我以前就这样)
日期问题可以看看我之前写过的博客:蓝桥杯备赛之日期问题-CSDN博客
代码
// 直接枚举2023年的所有日期即可
// 时间复杂度o(365*100)
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define f first
#define s second
const int N = 1e5 + 50;
int dates[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
void solve() {
int ans = 0;
int a[120];
for (int i = 1;i <= 100; i++) cin >> a[i];
for (int month = 1;month <= 12; month++) { // 枚举月份
for (int date = 1;date <= dates[month]; date++) { // 枚举日
int b[9] = {0,2,0,2,3,month/10,month%10,date/10,date%10};
int cnt = 1;
for (int i = 1;i <= 100; i++){
if (b[cnt] == a[i]) cnt++;
if (cnt == 9) {
ans++;
break;
}
}
}
}
cout << ans; // 235
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
while (t--) {
solve();
}
return 0;
}
T2:01串的熵
解题思路
思路: 0的个数比1少,直接枚举0的个数即可,时间复杂度最坏O(11666666),当然这是填空题,能出答案就行
注意:要求的H值的精度为小数点后四位,我们判断就判小数点后四位,过大或过小答案都会有偏差,不要认为精度越小答案越准确!!!
代码
#include <bits/stdc++.h>
#define int long long
#define f first
#define s second
#define all(x) x.begin(),x.end()
using namespace std;
const int N = 1e5 + 50;
const double P = 11625907.5798;
int sum = 23333333;
void solve() {
// c0 < c1
for (double i = sum/2; i >= 0; i--) { // 枚举0的个数
double p0 = i / sum; // 0的占比
double p1 = (sum - i) / sum; // 1的占比
double h = -(p0*i)*log2(p0)-(p1*(sum-i))*log2(p1);
// if (h - P <= 1e-6) { // 精度1e-6的答案为11027420
if (h - P <= 1e-4) {
cout << (int)i; // 11027421
return ;
}
}
return ;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
while (t--) {
solve();
}
return 0;
}
T3:冶炼金属
解题思路
思路:求解的是转化率的可能范围,即求区间的边界,二分其中一个边界,枚举另一个边界即可
代码
#include <bits/stdc++.h>
#define int long long
#define f first
#define s second
#define all(x) x.begin(),x.end()
using namespace std;
const int N = 1e4 + 50;
int n;
vector<pair<int,int> > a(N);
bool check(int mid) {
for (int i = 1;i <= n; i++) {
if (a[i].f / a[i].s < mid) return false; // 可以更小
}
return true;
}
void solve() {
cin >> n;
int maxx = 0;
for (int i = 1;i <= n; i++) {
cin >> a[i].f >> a[i].s;
maxx = max(maxx,a[i].f/a[i].s);
}
int l = 0,r = maxx+1; // 二分枚举转化率的最大值
while (l < r) {
int mid = (l + r + 1) >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
int max_x = r,min_x;
int f = 0;
for (int i = max_x;i >= 0; i--) { // 枚举最小转化率
for (int j = 1;j <= n; j++) {
if (a[j].f / i != a[j].s) {
min_x = i + 1;
f = 1;
break;
}
}
if (f) break;
}
cout << min_x << " " << max_x;
return ;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
while (t--) {
solve();
}
return 0;
}
T4:飞机降落
解题思路
首先我们看题目的范围,飞机架数和测试样例最多10,明显可以暴力求解!!!,下面提供两种暴力解法
- DFS:DFS搜索每一个飞机降落的时机,当所有飞机成功降落时递归结束,注意判重与回溯(常用)
- 全排列函数:直接对数组的下标取全排列,获得所有可能的飞机降落顺序,找到一种合法顺序即可返回YES
时间复杂度最坏:O(10*10!)
代码
DFS
// DFS
#include <bits/stdc++.h>
#define int long long
#define f first
#define s second
#define all(x) x.begin(),x.end()
using namespace std;
const int N = 1e5 + 50;
struct node {
int t; // 最早降落时间
int d; // 周旋时间
int l; // 降落花费的时间
}a[20];
int n;
bool st[N]; // 标记当前飞机是否降落
// 已经降落了几架飞机以及上一架飞机降落的时间
bool dfs(int u,int last) {
if (u >= n) return true; // 飞机全部安全降落,递归退出点
for (int i = 1;i <= n; i++) {
if (st[i]) continue;
// 1.判断当前飞机是否可以降落
if (a[i].t + a[i].d < last) return false; // 不能降落,回溯
// 2.思考u+1个位置由谁降落
st[i] = true;
if (dfs(u + 1,max(a[i].t,last) + a[i].l)) return true;
st[i] = false;
}
return false;
}
void solve() {
cin >> n; // 飞机架数
for (int i = 1;i <= n; i++) st[i] = false; // 初始化st数组
for (int i = 1;i <= n; i++) {
cin >> a[i].t >> a[i].d >> a[i].l;
}
if (dfs(0,0)) cout << "YES" << '\n';
else cout << "NO" << '\n';
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
int t; cin >> t;
while (t--) {
solve();
}
return 0;
}
全排列函数
// 只要找到一种可安全降落的方案即可
// nextpermutation做法
#include <bits/stdc++.h>
#define int long long
#define f first
#define s second
#define all(x) x.begin(),x.end()
using namespace std;
struct node {
int t; // 最早降落时间
int d; // 周旋时间
int l; // 降落花费的时间
}a[20];
int n;
void solve() {
cin >> n; // 飞机架数
int id[20];
for (int i = 1;i <= n; i++) {
cin >> a[i].t >> a[i].d >> a[i].l;
id[i] = i;
}
// 枚举飞机降落的所有可能情况
do {
int f = 0;
int last = a[id[1]].t + a[id[1]].l; // 上一架飞机完成降落的时间
// 判断当前是否为合法降落顺序
for (int j = 2;j <= n; j++) {
int i = id[j];
int now = a[i].t + a[i].d; // 当前飞机最晚降落时间
if (now >= last) { // 可以降落
// if (a[i].t > last) last = a[i].t + a[i].l;
// else last = last + a[i].l;
last = max(last,a[i].t) + a[i].l;
}
else { // 无法降落
f = 1;
break;
}
}
if (!f) {
cout << "YES" << '\n';
return ;
}
}while(next_permutation(id + 1,id + n + 1));
// 无法安全降落
cout << "NO" << '\n';
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
int t; cin >> t;
while (t--) {
solve();
}
return 0;
}
T6:岛屿个数
解题思路
前提:下面将外面一圈海称为外海,非子岛屿称为外岛,那么我们要求的是外岛的数量;
下面的地图中,(1,1)的0就是外海
0 1 1 1 1
1 1 0 0 1
1 0 1 0 1
1 0 0 0 1
1 1 1 1 1
我们从每一个外海开始做BFS,搜索到的岛的一定是外岛,然后标记该外岛,继续求下一个外岛;这里标记外岛既可以用DFS,也可以用BFS
注意:搜索外海是8个方向,搜索外岛是4个方向,当没有外海时整体就是一个外岛;
比如一个5*5的地图,并没有外海,所以直接输出答案1即可
1 1 1 1 1
1 0 0 0 1
1 0 1 0 1
1 0 0 0 1
1 1 1 1 1
代码:
#include <bits/stdc++.h>
#define int long long
#define f first
#define s second
#define all(x) x.begin(),x.end()
using namespace std;
using pii = pair<int,int>;
const int N = 55;
int dx[] = {0,0,0,-1,1,-1,1,-1,1};
int dy[] = {0,1,-1,0,0,1,1,-1,-1};
int g[N][N],st_sea[N][N],st_land[N][N];
int n,m,ans;
// 判边界
bool check(int x, int y) {
return (x >= 1 && x <= n && y >= 1 && y <= m);
}
// BFS标记外海
void bfs_land(int x,int y) {
queue<pii> q;
q.push({x,y});
while (q.size()) {
auto p = q.front(); q.pop();
for (int i = 1;i <= 4; i++) {
int nx = p.f + dx[i];
int ny = p.s + dy[i];
if (!check(nx,ny) || st_land[nx][ny] || !g[nx][ny]) continue;
st_land[nx][ny] = 1;
q.push({nx,ny});
}
}
}
// DFS标记外岛
void dfs_land(int x,int y) {
for (int i = 1;i <= 4; i++) { // 4个方向
int nx = x + dx[i];
int ny = y + dy[i];
if (!check(nx,ny) || st_land[nx][ny] || !g[nx][ny]) continue ;
st_land[nx][ny] = 1;
dfs_land(nx,ny);
// 不需要回溯,标记陆地一条路走到黑就行了
}
}
// 遍历外海
void bfs_sea(int x,int y) {
queue<pii> q;
q.push({x,y});
st_sea[x][y] = 1;
while (q.size()) {
auto p = q.front(); q.pop();
for (int i = 1;i <= 8; i++) { // 8个方向
int nx = p.f + dx[i];
int ny = p.s + dy[i];
if (!check(nx,ny)) continue;
// (1) 外海
if (!st_sea[nx][ny] && !g[nx][ny]) {
st_sea[nx][ny] = 1;
q.push({nx,ny});
}
// (2) 外岛
if (!st_land[nx][ny] && g[nx][ny]) {
st_land[nx][ny] = 1;
ans++;
bfs_land(nx,ny);
}
}
}
}
void solve() {
cin >> n >> m;
ans = 0;
for (int i = 1;i <= n; i++) {
string s; cin >> s; s = " " + s;
for (int j = 1;j <= m; j++) {
g[i][j] = s[j] - '0';
// 顺便初始化
st_sea[i][j] = st_land[i][j] = 0;
}
}
int flag = 0; // 标记是否存在外海
for (int i = 1;i <= n; i++) {
for (int j = 1;j <= m; j++) {
if (i==1||i==n||j==1||j==m) { // 是外海
if (!st_sea[i][j] && !g[i][j]) { // 未被访问过
flag = 1;
bfs_sea(i,j);
}
}
}
}
if (!flag) cout << "1" << '\n'; // 无外海
else cout << ans << '\n';
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
int t; cin >> t;
while (t--) {
solve();
}
return 0;
}
T7:子串简写
解题思路
简单前缀和
说明蓝桥杯题目难度不是有序的,大家要每个题都看一看
代码
#include <bits/stdc++.h>
#define int long long
#define f first
#define s second
#define all(x) x.begin(),x.end()
using namespace std;
const int N = 1e5 + 50;
void solve() {
int ans = 0;
int k; cin >> k;
string s; cin >> s;
char c1,c2; cin >> c1 >> c2;
int t = 0;
for (int i = 0;i < (int)s.size(); i++) {
if (s[i] == c1) t++;
if (s[i + k - 1] == c2) ans += t; // 前面的c1均可与该c2组成合法答案
}
cout << ans;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
while (t--) {
solve();
}
return 0;
}
T8:整数删除
解题思路
涉及两个操作
- 动态查找最小值
使用小根堆的优先队列维护最小值
priority_queue<pii, vector<pii>, greater<pii> > q; // 小根堆
- 删除某一个值
使用双链表维护一个结点的左右结点
对于下标为i的结点,一开始是这个状态
l[i] = i - 1; r[i] = i + 1;
删除一个结点,直接将该结点的左节点指向右节点,右节点指向左节点
r[l[i]] = r[i]; l[r[i]] = l[i];
然后要注意的是:
删除操作之后a数组的值会改变,同时也要更新优先队列中的值,更新方式是取出一个最小值时,如果当前优先队列中的值与a数组中的值不同,就将新的值加入优先队列(优先队列中的那个值也会被及时pop()出去),该次不做删除操作;
if (num != a[d]) { // a数组中该值已经改变,优先队列中也要更新 q.push({a[d],d}); continue; }
举个例子:
a[] = {1,2,3} k = 2
q = { {1,1}, {2,2}, {3,3} }
第一次:从q中取出1,1 = a[1],不用操作,删除该值
a[] = {-1,3,3}
q = { {2,2},{3,3} }
第二次:从q中取出2,a[2]已经更新为3,2 != a[2],将{a[2],2}push到q中
a[] = {-1,3,3}
q = { {3,2} {3,3} }
第三次:从q中取出3,3 = a[2],不用操作,删除该值
a[] = {-1,-1,6}
q = { {3,3} }
第四次 重复第二次的操作...
有人可能会问为什么第3次删除第二个元素的时候,只对3 加了3,为什么1号元素-1不会加?
因为1号结点被删除后,2号结点的左结点已经不是1号结点了(这里为空),所以也并不会对它进行操作;
代码
#include <bits/stdc++.h>
#define int long long
#define f first
#define s second
#define all(x) x.begin(),x.end()
using namespace std;
using pii = pair<int,int>;
const int N = 5e5 + 50;
int a[N],l[N],r[N];
priority_queue<pii, vector<pii>, greater<pii> > q; // 小根堆
void solve() {
int n,k; cin >> n >> k;
for (int i = 1; i <= n; i++) {
cin >> a[i];
q.push({a[i], i}); // 因为相同要先删前面的,所以下标也要存
l[i] = i - 1; // 当前点左边点的下标
r[i] = i + 1; // 当前点右边点的下标
}
while (k) {
auto p = q.top(); q.pop();
int num = p.f; // 值
int d = p.s; // 下标
if (num != a[d]) { // a数组中该值已经改变,优先队列中也要更新
q.push({a[d],d});
continue;
}
// 左右加上值
a[l[d]] += num;
a[r[d]] += num;
// 删除该结点
r[l[d]] = r[d];
l[r[d]] = l[d];
a[d] = -1; // 标记已经被删除
k--;
}
for (int i = 1;i <= n; i++) {
if (a[i] != -1) cout << a[i] << " ";
}
return ;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
while (t--) {
solve();
}
return 0;
}
T9:景区导游
解题思路
LCA+树上前缀和模板题
注意一个性质:两个点之间的路径一定经过他们的LCA
树上前缀和求的是根结点到某个结点的距离,那么两个结点的距离就为:
sum[a] + sum[b] - 2*sum[LCA(a,b)]
1.如果去掉的是两边的景点,就减去当前景点和它左/右边景点的距离
2.不是两边就要减掉它和左边右边两个景点的距离,剪多的要加回来
代码
// 树上前缀和 + LCA
// s(a,b) = sum[a] + sum[b] - 2*(sum[LCA(a,b)])
// s(a,b)是a,b之间的距离
// sum记录的是根节点到该点的距离
#include <bits/stdc++.h>
#define int long long
#define f first
#define s second
#define all(x) x.begin(),x.end()
using namespace std;
const int N = 1e5 + 50;
struct Edge {
int to,next,w;
}edge[N*2];
int head[2*N],cnt;
void add(int u,int v,int w) {
edge[++cnt].w = w;
edge[cnt].to = v;
edge[cnt].next = head[u];
head[u] = cnt;
}
//-----以上为链式前向星-----//
int n,k;
int deep[N],fa[N][30];
int sum[N]; // 前缀和数组
int st[N]; // 标记数组
void dfs(int x,int father) {
deep[x] = deep[father] + 1; // 当前结点的深度=父节点+1
fa[x][0] = father;
for (int i = 1;(1<<i) <= deep[x]; i++) {
fa[x][i] = fa[fa[x][i-1]][i-1];
}
for (int i = head[x];i;i = edge[i].next) {
if (edge[i].to != father) {
dfs(edge[i].to,x); // 颠鸾倒凤
}
}
}
void dfs1(int u, int sum1) {
st[u] = 1;
sum[u] = sum1;
for (int i = head[u];i;i = edge[i].next) {
int v = edge[i].to;
int w = edge[i].w;
if (!st[v]) dfs1(v,sum1 + w);
}
}
int LCA(int a,int b) {
if (deep[a] < deep[b]) swap(a,b); // 始终使a的深度更深
// 1.将a跳到和b一样的深度
for (int i = 19; i >= 0; i--) {
if (deep[a] - (1<<i) >= deep[b]) {
a = fa[a][i];
}
if (a == b) return a;
}
// 2.a,b一起向上跳
for (int i = 19; i >= 0; i--) {
if (fa[a][i] != fa[b][i]) {
a = fa[a][i];
b = fa[b][i];
}
}
return fa[a][0];
}
void solve() {
cin >> n >> k;
int a[k + 1]; // 原路线
for (int i = 1;i <= n - 1; i++) {
int u,v,w; cin >> u >> v >> w;
add(u,v,w); add(v,u,w);
}
dfs(1,0); // dfs获得结点深度
dfs1(1,0); // 获得树上前缀和
for (int i = 1;i <= k; i++) cin >> a[i];
// 计算源路径总和
int all = 0;
for (int i = 1;i <= k - 1; i++) {
all += sum[a[i]] + sum[a[i + 1]] - 2*sum[LCA(a[i],a[i + 1])];
}
// 计算去掉第i个景点后的答案
for (int i = 1;i <= k; i++) {
int ans = all;
if (i == 1) { // 减去1~2之间的距离
ans -= sum[a[1]] + sum[a[2]] - 2*sum[LCA(a[1],a[2])];
}
else if (i == k) { // 减掉k-1~k之间的距离
ans -= sum[a[k - 1]] + sum[a[k]] - 2*sum[LCA(a[k-1],a[k])];
}
else { // 减掉两边,加上中间减多的
ans -= sum[a[i - 1]] + sum[a[i]] - 2*sum[LCA(a[i-1],a[i])];
ans -= sum[a[i + 1]] + sum[a[i]] - 2*sum[LCA(a[i+1],a[i])];
ans += sum[a[i - 1]] + sum[a[i + 1]] - 2*sum[LCA(a[i-1],a[i+1])];
}
cout << ans << " ";
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
while (t--) {
solve();
}
return 0;
}
T10:砍树
解题思路
LCA+树上差分模板题
如果将m组点连成m条路经,那么只有删除m条路径中的公共边才能使m组点不连通,那我们用树上差分求每条被路径经过的次数即可,次数为m且编号最大的为答案
代码
// 将m对点连成一条一条的路径,那么答案就是每一个路径都经过的边中编号最大的边
// 两点的路径一定经过LCA,那么求出每一条被路径经过的次数即可
// 次数为m的就是可行答案,取编号最大的可行答案
#include <bits/stdc++.h>
#define int long long
#define f first
#define s second
#define all(x) x.begin(),x.end()
using namespace std;
const int N = 1e5 + 50;
struct Edge {
int to,next;
}edge[N*2];
int head[2*N],cnt;
void add(int u,int v) {
edge[++cnt].to = v;
edge[cnt].next = head[u];
head[u] = cnt;
}
//-----以上为链式前向星-----//
int n,m;
int deep[N],fa[N][30];
int s[N]; // 差分数组
map<pair<int,int>,int> mp;
// 1.计算深度
void dfs(int x,int father) {
deep[x] = deep[father] + 1;
fa[x][0] = father;
for (int i = 1;(1<<i) <= deep[i]; i++) {
fa[x][i] = fa[fa[x][i-1]][i-1];
}
for (int i = head[x];i;i = edge[i].next) {
if (edge[i].to != father) dfs(edge[i].to, x);
}
}
// 2.计算lca
int lca(int a,int b) {
if (deep[a] < deep[b]) swap(a,b);
// 1.跳到同一高度
for (int i = 19;i >= 0; i--) {
if (deep[a] - (1<<i) <= deep[b])
a = fa[a][i];
if (a == b) return a;
}
// 2.一起向上跳
for (int i = 19;i >= 0; i--) {
if (fa[a][i] != fa[b][i]) {
a = fa[a][i],b = fa[b][i];
}
}
return fa[a][0];
}
// 3.计算前缀和
int res[N];
int cal_sum(int u,int fa) {
for (int i = head[u];i;i = edge[i].next) {
int it = edge[i].to;
if (it != fa) {
s[u] += cal_sum(it,u);
}
}
res[mp[{fa,u}]] = s[u];
return s[u];
}
//4.实现函数
void solve() {
cin >> n >> m;
for (int i = 1;i <= n - 1; i++) {
int u,v; cin >> u >> v;
add(u,v),add(v,u);
mp[{u,v}] = mp[{v,u}] = i;
}
dfs(1,0);
for (int i = 1;i <= m; i++) {
int a,b; cin >> a >> b;
s[a]++,s[b]++;
s[lca(a,b)]-=2;
}
cal_sum(1,1);
int ans = -1;
for (int i = 1;i <= n; i++) {
if (res[i] == m) {
ans = max(ans,i);
}
}
cout << ans;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
while (t--) {
solve();
}
return 0;
}
最后还有一件事,如果你是C++/C语言,一定要写return 0;这很重要!!!