第十四届蓝桥杯省赛
- 整数删除
-
- 满分思路及代码
-
- [solution1 (40% 双指针暴力枚举)](#solution1 (40% 双指针暴力枚举))
- [solution 2(优先队列+模拟链表 AC)](#solution 2(优先队列+模拟链表 AC))
- 冶炼金属
- 子串简写
-
- 满分思路及代码
-
- [solution 1(60% 双指针)](#solution 1(60% 双指针))
- [solution 2(二分 AC)](#solution 2(二分 AC))
- 岛屿个数
- 飞机降落
- 接龙数列
整数删除
满分思路及代码
solution1 (40% 双指针暴力枚举)
cpp
#include <iostream>
using namespace std;
const int N=5e5+10;
int n,k;
int a[N];
int main()
{
cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
//实际上我们并不能通过简单的排序来找到数列中最小的整数
//因为这样会失去原来的相对位置 而导致删去后的增加操作出错
//所以我们可以用双指针的思路来写
while(k--)
{
int l=1;
int r=n;
while(l!=r)
{
if(a[l]<=a[r])
{
r--;
}else{
l++;
}
}//遍历整个数组寻找最小值
if(l==1)
{
a[l+1]+=a[l];
}else if(l==n)
{
a[l-1]+=a[l];
}else{
a[l+1]+=a[l];
a[l-1]+=a[l];
}
//删除 覆盖掉
for(int i=l;i<=n;i++)
{
a[i]=a[i+1];
}
n--;
}
for(int i=1;i<=n;i++)
{
cout<<a[i]<<' ';
}
return 0;
}
solution 2(优先队列+模拟链表 AC)
思路参考:
cpp
#include <bits/stdc++.h>
using namespace std;
// 记录每个位置的数是否被删除过
bool removed[500005];
// 数据过大,爆int
#define ll long long
int main()
{
int n, k;
cin >> n >> k;
// 数列,数和左右相邻的数的距离
vector<pair<ll, pair<int, int>>> a(n);
// 优先队列,数和下标
priority_queue<pair<ll, int>, vector<pair<ll, int>>, greater<pair<ll, int>>> p;
int t;
for (int i = 0; i < n; i++)
{
cin >> t;
// 相邻的数初始距离为1
a[i] = {t, {1, 1}};
// 数和下标压入堆
p.push({t, i});
}
//查找k次
while (k)
{
// 弹出第一个数
pair<ll, int> tp = p.top();
p.pop();
ll value = tp.first;
int index = tp.second;
// 如果已经被删除过,或者被修改过,则跳过,否则删除
if (removed[index] || a[index].first != value)
continue;
// 标记为已删除
removed[index] = true;
// 左右最近未被删除节点到自己的距离
int l = index - a[index].second.first;
int r = index + a[index].second.second;
// 如果左边相邻的数存在则更新
if (l >= 0 && !removed[l])
{
// 更新左边的数并压入堆
a[l].first += value;
p.push({a[l].first, l});
// 如果右边的数存在,则更新左边的数到右边的距离
if (r < n && !removed[r])
{
a[l].second.second += a[index].second.second;
}
}
// 如果右边相邻的数存在则更新
if (r < n && !removed[r])
{
// 更新右边的数并压入堆
a[r].first += value;
p.push({a[r].first, r});
// 如果左边的数存在,则更新右边的数到左边的距离
if (l >= 0 && !removed[l])
{
a[r].second.first += a[index].second.first;
}
}
k--;
}
// 输出还未被删除的数
for (int i = 0; i < n; i++)
{
if (!removed[i])
cout << a[i].first << " ";
}
return 0;
}
冶炼金属

满分代码及思路
//对于这个v的范围我们怎么来确定呢
//我觉得是这样的 例如投入75个O产出3个X
//如果最好的情况即浪费的最少得时候 即V=25 当然也有小数的情况 但也就是直接相除之后向下取整就好 最后对于每一次冶炼的V取max
//最坏的情况需要考虑 还是拿上面的举例 我们可以从产出4个X所需要的V枚举到3 最后取min 但是注意此时就需要向上取整即加1;
//因为我们是在找不符合75个出3个X的临界点 即多少O出4个X
cpp
#include <bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int a[N],b[N];
int n;
int maxval=1e9;
int minval=0;
int main()
{
cin>> n;
for(int i=0;i<n;i++)
{
cin>>a[i]>>b[i];
}
for(int i=0;i<n;i++)
{
maxval=min(a[i]/b[i],maxval);
minval=max(a[i]/(b[i]+1)+1,minval);
}
cout<<minval<<' '<<maxval;
return 0;
}
子串简写

满分思路及代码
solution 1(60% 双指针)
cpp
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
// 双指针方法
int TwoPointers(int K, const string& S, char c1, char c2) {
int n = S.length();
int count = 0;
for (int left = 0; left < n; ++left) {
if (S[left] == c1) {
for (int right = left + K - 1; right < n; ++right) {
if (S[right] == c2) {
++count;
}
}
}
}
return count;
}
//首先暴力的思路类似于双指针
//三种情况
//我们用p指针找到C1 保持c1不动移动指针q 让q不断去找c2 直到满足我们的长度我们的res++
//然后q走到最后一个c2了之后移动p 规则一样
//p q一起动
//
int main() {
int K;
string S;
char c1, c2;
// 读取输入
cin >> K;
cin >> S >> c1 >> c2;
// 计算结果
int result=TwoPointers(K, S, c1, c2);
cout << result << endl;
return 0;
}
solution 2(二分 AC)
cpp
#include<iostream>
#include<algorithm>
using namespace std;
int nec1[500100];
int c1dx = 0;
int c2dx = 0;
int nec2[500100];
int main()
{
int k;
cin >> k;
string str;
cin >> str;
str = "1" + str;
char c1, c2;
cin >> c1 >> c2;
for (int i = 1; i < str.length(); i++) {
//记录c1字符所有出现的位置
if (str[i] == c1) {
nec1[c1dx] = i;
c1dx++;
}
//记录c2字符所有出现位置
if (str[i] == c2) {
nec2[c2dx] = i;
c2dx++;
}
}
long long ans = 0;
//枚举c1字符出现的位置
for (int i = 0; i < c1dx; i++) {
//二分查找c2字符出现的位置的合法位置
auto dx = lower_bound(nec2, nec2 + c2dx, nec1[i] + k-1);
//没有找到情况
if (dx == nec2 + c2dx)continue;
//找到了
int t = dx - nec2;
//加上总合法个数
ans += c2dx - t;
}
cout << ans << endl;
return 0;
}
岛屿个数

满分代码及思路
在我之前的文章 搜索系列 里面可以找到详细分析
cpp
#include <bits/stdc++.h>
using namespace std;
const int X = 50 + 10;
typedef pair<int, int> PII;
int grid[X][X]; // 使用 int 类型数组
int n, m, T;
int ans;
int s[X];
// 海的移动偏移量数组
int dx[8] = {-1,-1,-1,0,1,1,1,0};
int dy[8] = {-1,0,1,1,1,0,-1,-1};
// 陆地的移动偏移量
int DX[4] = {-1, 0, 1, 0};
int DY[4] = {0, 1, 0, -1};
bool st_land[X][X];
bool st_sea[X][X];
bool check(int x, int y) {
if (x < n && x >= 0 && y < m && y >= 0) {
return true;
}
return false;
}
// 找到了(x,y)所在的岛屿 并将该岛屿的所有点都标为了 true
void bfs_land(int u, int v) {
queue<PII> Q;
st_land[u][v] = true;
PII tp = {u, v};
Q.push(tp);
while (!Q.empty()) {
PII t = Q.front();
Q.pop();
for (int i = 0; i < 4; i++) {
int nu = t.first + DX[i];
int nv = t.second + DY[i];
if (check(nu, nv) && grid[nu][nv] == 1 && !st_land[nu][nv]) {
st_land[nu][nv] = true;
Q.push({nu, nv});
}
}
}
}
void bfs_sea(int x, int y) {
queue<PII> q;
PII tmp = {x, y};
q.push(tmp);
st_sea[x][y] = true;
while (!q.empty()) {
PII p = q.front();
q.pop();
for (int i = 0; i < 8; i++) { // 枚举一下海的方向
int nx = p.first + dx[i];
int ny = p.second + dy[i];
if (!check(nx, ny) || st_sea[nx][ny]) {
continue;
}
if (grid[nx][ny] == 0) {
st_sea[nx][ny] = true;
q.push({nx, ny});
} else if (grid[nx][ny] == 1 && !st_land[nx][ny]) {
ans++;
bfs_land(nx, ny); // 如果发现寻找外海的过程中发现了陆地 说明属于新的外岛那么需要找到与他相连的全部陆地
}
}
}
}
int main() {
cin >> T;
while (T--) {
cin >> n >> m;
ans = 0; // 每次都需要重置一下
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
st_sea[i][j] = st_land[i][j] = false;
}
}
for (int i = 0; i < n; i++) {
string s;
cin >> s;
for (int j = 0; j < m; j++) {
grid[i][j] = s[j] - '0'; // 将字符转化成数字存进去
}
}
bool has_sea = false;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (i == 0 || i == n - 1 || j == 0 || j == m - 1) {
if (!st_sea[i][j] && grid[i][j] == 0) { // 当前的这个点是海并且是没有被标记过
has_sea = true;
bfs_sea(i, j);
}
}
}
}
// 判断全是陆地的特例(只有一个大岛)
if (!has_sea) {
ans = 1;
}
cout << ans << endl;
}
return 0;
}
飞机降落
满分思路及代码
思路直接去看我之前的文章就好:搜索系列
非常详细具体
视频讲解:非常牛逼
cpp
#include <bits/stdc++.h>
using namespace std;
const int N =10+9;//防止越界
int n;//有多少架飞机
//int T,D,L;//分别表示降落时刻,能盘旋的时间,降落的时间
//考虑到这题这三个数据可以代表一架飞机的属性 可以考虑采用结构体来实现
struct plane{
int t;
int d;
int l;
}p[N];
bool s[N];//每架飞机的状态
//我们需要知道几架飞机成功降落 和前一架飞机降落的时间
bool dfs(int x,int time)
{
if(x>=n)
{
return true;
}//递归出口
//枚举每种降落顺序
for(int i=0;i<n;i++)
{
if(!s[i])//这架飞机没有安排过
{
s[i]=true;
if(p[i].t+p[i].d<time)//燃尽了也没等到前一架飞机降落完毕
{
//回溯
s[i]=false;
return false;
}
int next=max(p[i].t,time)+p[i].l;//核心 两种情况的综合
//即一来就可以落 和 需要等前一架降落完毕再落
if(dfs(x+1,next))
{
return true;//后续的飞机都可以顺利降落
}
s[i]=false;
}
}
return false;//所有降落方案都不能成功降落;
}
int main()
{
int T;//测试的组数
cin>>T;
while(T--)
{
cin>>n;
for(int i=0;i<n;i++)
{
cin>>p[i].t>>p[i].d>>p[i].l;
}
if(dfs(0,0))
{
cout<<"YES"<<endl;
}else{
cout<<"NO"<<endl;
}
for(int i=0;i<n;i++)
{
s[i]=false;
}
}
return 0;
}
接龙数列

满分思路及代码
其实我们需要转化题目所问的问题
题目问的是 最少需要删除几个字符或者说数字 等价于求数列最长是多少 ------最长子序列问题
思路参考:视频讲解
cpp
#include <iostream>
using namespace std;
const int N = 1e5;
int a[N]; //也可以使用vector,但在效率上会有损失
int dp[10];//全局变量,全部默认初始化为0
int main()
{
int n;
cin >> n;
for (int i = 0;i < n;i++)
{
cin >> a[i];
}
//拆分每一个数,取出首位数字和末位数字
int front, tail;
for (int i = 0;i < n;i++)
{
int cur = a[i];
tail =cur % 10;
front =tail;
while (cur)
{
front = cur%10;
cur= cur / 10;
}
dp[tail] = dp[tail] > dp[front] + 1 ? dp[tail] : dp[front ]+ 1;
}
//找最大的dp数组元素值
int maxLen = 0;
for (int i = 0;i < 10;i++)
{
if (dp[i] > maxLen)
maxLen = dp[i];
}
cout << n - maxLen << endl;
return 0;
}