第十四届蓝桥杯大赛软件赛省赛C/C++ 大学 B 组(补题)

文章目录

    • [1 日期统计](#1 日期统计)
    • [2 01串的熵](#2 01串的熵)
    • [3 冶炼金属](#3 冶炼金属)
    • [4 飞机降落](#4 飞机降落)
    • [5 接龙数列](#5 接龙数列)
    • [6 岛屿个数](#6 岛屿个数)
    • [7 子串简写](#7 子串简写)
    • [8 整数删除](#8 整数删除)
    • [9 景区导游](#9 景区导游)
    • [10 砍树](#10 砍树)

前言:时隔一年,再次做这套题(去年参赛选手),差点道心不稳T_T,故作此补题!

1 日期统计

  • 没写出来,看题解知道了一种暴力的思想,枚举所有2023年的日期,看看有没有满足条件的数。
  • 关于如何提取题中的数字?可以复制到excel当中,然后ctrl+H,将空格替换为英文逗号!
cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define endl "\n"
#define int long long
typedef pair<int,int> PII;
const int N = 2e5+10, INF = 0x3f3f3f3f;
int a[N]={0,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};
int mon[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
int n=100,ans;

void solve() {
	for(int m=1;m<=12;m++) //枚举所有日期
	  for(int d=1;d<=mon[m];d++){
	  	int tar[9]={0,2,0,2,3,m/10,m%10,d/10,d%10}; //当前日期
	  	int num=1;
	  	for(int i=1;i<=100;i++){ //看看有没有符合条件的
	  		if(a[i]==tar[num]){
	  			num++;
	  			if(num==9){
	  				ans++; break;
				}
			}
		  }
	  }
	cout<<ans;
}

signed main() {
//	IOS;
	int T=1;
//	cin>>T;
	while(T--) {
		solve();
	}
	return 0;    
}

2 01串的熵

  • 因为0的出现次数比1的出现次数少,所以我们可以枚举0的个数,1的个数即为01串的长度减去0的个数。
  • 然后根据公式即可求得
cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define endl "\n"
#define int long long
const int N = 2e5+10, INF = 0x3f3f3f3f;
int len=23333333,cnt;
double res,tar=11625907.5798; 

void solve() {
	for(int i=0;i<=len;i++){ //0的数量 
		int j=len-i; //1的数量 
		res=-1.0*(1.0*i/len)*i*(log(1.0*i/len)/log(2))+(-1)*(1.0*j/len)*j*(log(1.0*j/len)/log(2));
		if(res>=tar){
			printf("%.4f %.4f\n",res,tar);
			cout<<i<<endl; break;
		}
	}
	
}

signed main() {
//	IOS;
	int T=1;
//	cin>>T;
	while(T--) {
		solve();
	}
	return 0;    
}

3 冶炼金属

  • 读完题目后,我们可以枚举答案来求得V的最大值和最小值,即用到二分答案。
cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define endl "\n"
#define int long long
const int N = 1e4+10, INF = 0x3f3f3f3f;
int n,a[N],b[N];
int res1,res2;

bool check1(int x){
	for(int i=1;i<=n;i++){
		int num=a[i]/x; //转换的个数
		if(num>b[i]) return false;
	}
	return true;
}

int calc1(){
	int l=0,r=1e9+10;
	while(l+1<r){
		int mid=l+r>>1;
		if(check1(mid)) r=mid; //满足条件,缩小范围求最小值
		else l=mid;
	}
	return r;
}

bool check2(int x){
	for(int i=1;i<=n;i++){
		int num=a[i]/x;
		if(num<b[i]) return false;
	}
	return true;
}

int calc2(){
	int l=0,r=1e9+10;
	while(l+1<r){
		int mid=l+r>>1;
		if(check2(mid)) l=mid;
		else r=mid;
	}
	return l;
}

void solve() {
	cin>>n;
	for(int i=1;i<=n;i++)
	  cin>>a[i]>>b[i];
	res1=calc1(); //最小值
	res2=calc2(); //最大值
	cout<<res1<<' '<<res2;
}

signed main() {
//	IOS;
	int T=1;
//	cin>>T;
	while(T--) {
		solve();
	}
	return 0;    
}

4 飞机降落

  • N的数据范围只有10,我们可以暴力枚举所有飞机降落的顺序,如果有一个满足就符合要求。
  • 用全排列函数next_permutation函数可以实现这一操作
cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define endl "\n"
#define int long long
const int N = 1e3+10, INF = 0x3f3f3f3f;
struct node{
	int t,d,l;
}a[N];
int n,id[N]; 

void solve() {
	cin>>n;
	for(int i=1;i<=n;i++){
		int t,d,l; cin>>t>>d>>l;
		a[i]={t,d,l}; id[i]=i;
	}
	do{
		int now=0,flag=1;
		for(int i=1;i<=n;i++){
			int t=a[id[i]].t,d=a[id[i]].d,l=a[id[i]].l;
			if(t+d<now){ //不符合
				flag=0;
				break;
			} 
			else{
				if(t>now) now=t+l;
				else now+=l;
			}
		}
		if(flag){
			cout<<"YES"<<endl;
			return;
		}
	}while(next_permutation(id+1,id+1+n)); 
	cout<<"NO"<<endl;
}

signed main() {
//	IOS;
	int T=1;
	cin>>T;
	while(T--) {
		solve();
	}
	return 0;    
}

5 接龙数列

  • 可以将问题转化为求最长的符合要求的接龙序列,然后用总个数减去最长的接龙序列的长度。
  • 这就需要dp来解决了
cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define endl "\n"
#define int long long
const int N = 1e5+10, INF = 0x3f3f3f3f;
int n,num=0;
int x,dp[N];
//dp[i]表示以i为结尾的最长接龙序列的长度 

void solve(){
	cin>>n;
	string s;
	for(int i=0;i<n;i++){
		cin>>s;
		int x=s[0]-'0',y=s[s.size()-1]-'0'; //取出第一位和最后一位 
		dp[y]=max(dp[y],dp[x]+1);
		num=max(num,dp[y]);
	}
	cout<<n-num;
}

signed main() {
//	IOS;
	int T=1;
//	cin>>T;
	while(T--) {
		solve();
	}
	return 0;    
}

6 岛屿个数

  • 连通块,图的遍历问题
  • 可以将所有的连通块染色,每一种颜色代表一个连通块。
  • 然后检查所有的岛屿是不是子岛屿,即该岛屿是否在"环"的内部,如何判断是否在环的内部呢?
  • 如果该岛屿内的任意一个点可以走到边界或边界之外,说明该岛屿不在环内,可以把其它的岛屿看作障碍物;如果在环内,则不可能到达边界点!
  • 判断是否为子岛屿时,需要沿着8个方向走!
cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define endl "\n"
#define int long long
#define fi first
#define se second
typedef pair<int,int> PII;
const int N = 1e3+10, INF = 0x3f3f3f3f;
char g[N][N];
int m,n,cnt,ans;
int c[N][N]; 
int dx[]={0,1,0,-1,-1,1,1,-1};
int dy[]={1,0,-1,0,1,1,-1,-1};
set<int> s;

void bfs(int sx,int sy){
	cnt++; //该连通块染色为cnt
	queue<PII> q;
	q.push({sx,sy});
	while(q.size()){
		auto t=q.front(); q.pop();
		c[t.fi][t.se]=cnt;
		for(int i=0;i<4;i++){
			int tx=t.fi+dx[i];
			int ty=t.se+dy[i];
			if(tx<=0||tx>m||ty<=0||ty>n) continue;
			if(c[tx][ty]||g[tx][ty]=='0') continue;
			q.push({tx,ty});
		}
	}
}

bool vis[N][N],flag;
//判断是否在环内
bool check(int x,int y,int col){
	if(flag) return true;
	for(int i=0;i<8;i++){
		int tx=x+dx[i];
		int ty=y+dy[i];
		if(c[tx][ty]!=col&&c[tx][ty]) continue;
//		if(col==3) cout<<tx<<' '<<ty<<' '<<flag<<endl;
		if(tx<=1||ty<=1||tx>=m||ty>=n){ //到达边界,不在环内
			flag=1;
			return true;
		}
		if(vis[tx][ty]) continue; //已访问过
		vis[tx][ty]=1;
		if(check(tx,ty,col)) return true;
	}
	return false; //在环内
}

void solve() {
	cin>>m>>n;
	for(int i=1;i<=m;i++)
	  for(int j=1;j<=n;j++)
	    cin>>g[i][j];
	cnt=0; ans=0; s.clear(); //多组样例!注意清空!
	memset(c,0,sizeof(c));
	for(int i=1;i<=m;i++)
	  for(int j=1;j<=n;j++){
	  	if(!c[i][j]&&g[i][j]=='1'){
	  		bfs(i,j);
		}
	  }
//	cout<<endl;
//	for(int i=1;i<=m;i++){
//		for(int j=1;j<=n;j++)
//		  cout<<c[i][j];
//		cout<<endl;
//	}
    set<int> s; //set去重
	for(int i=1;i<=m;i++)
	  for(int j=1;j<=n;j++){
//	  	cout<<i<<' '<<j<<' '<<c[i][j]<<endl;
	    if(g[i][j]=='1'&&!s.count(c[i][j])&&check(i,j,c[i][j])){
			s.insert(c[i][j]);
		}
		memset(vis,0,sizeof(vis)); flag=0;
	}
	cout<<s.size()<<endl;
}

signed main() {
//	IOS;
	int T=1;
	cin>>T;
	while(T--) {
		solve();
	}
	return 0;    
}

7 子串简写

  • 我们可以记录两个字符a,b的所有位置,然后枚举第一个字符a,二分找到第一个符合要求的字符b的位置,之后所有的字符b都是符合要求的,累加所有答案即可
  • 复杂度为O(n log(n))
cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define endl "\n"
#define int long long
#define lb lower_bound
typedef pair<int,int> PII;
const int N = 2e5+10, INF = 0x3f3f3f3f;
int k,ans;
string s;
set<int> s1;
vector<int> a;
char c1,c2;

void solve() {
//	freopen("in.txt","r",stdin); 
	cin>>k>>s>>c1>>c2;
	for(int i=0;i<s.size();i++){
		if(s[i]==c1) s1.insert(i); //保存所有位置
		if(s[i]==c2) a.push_back(i);
	}
	for(auto i:s1){
		int cnt=lb(a.begin(),a.end(),i+k-1)-a.begin(); //二分查找
		ans+=a.size()-cnt;
	}
//	freopen("out.txt","w",stdout); 
	cout<<ans;
}

signed main() {
//	IOS;
	int T=1;
//	cin>>T;
	while(T--) {
		solve();
	}
	return 0;    
}

8 整数删除

  • 优先队列+链表
  • 每次取出数列中的最小值,可以用优先队列实现。
  • 删除和相邻两数加上被删除的值这两个操作用链表来实现。
  • 代码见蓝桥发题解的大佬

9 景区导游

  • 不会T_T
  • 用LCA来求解

10 砍树

  • 树上差分模板题
相关推荐
qystca10 分钟前
洛谷 P11242 碧树 C语言
数据结构·算法
冠位观测者17 分钟前
【Leetcode 热题 100】124. 二叉树中的最大路径和
数据结构·算法·leetcode
悲伤小伞22 分钟前
C++_数据结构_详解二叉搜索树
c语言·数据结构·c++·笔记·算法
m0_675988231 小时前
Leetcode3218. 切蛋糕的最小总开销 I
c++·算法·leetcode·职场和发展
佳心饼干-3 小时前
C语言-09内存管理
c语言·算法
dbln4 小时前
贪心算法(三)
算法·贪心算法
songroom4 小时前
Rust: offset祼指针操作
开发语言·算法·rust
code04号5 小时前
C++练习:图论的两种遍历方式
开发语言·c++·图论
煤泥做不到的!6 小时前
挑战一个月基本掌握C++(第十一天)进阶文件,异常处理,动态内存
开发语言·c++
F-2H6 小时前
C语言:指针4(常量指针和指针常量及动态内存分配)
java·linux·c语言·开发语言·前端·c++