2022年第十三届蓝桥杯大赛软件类省赛C/C++大学B组真题

打卡第七篇。

C.刷题统计

 

(非满分版) 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	ll a,b,n;cin>>a>>b>>n;
	ll num=5*a+2*b;
	ll i=1;
	ll flag=0;
	while(1){
		if(num*i>=n){
			flag=i;
			break;
		}
		i++;
	}
	ll day=0;
	if(num*flag==n)day=flag*7;
	else{
		ll sum=num*(flag-1);
		day=(flag-1)*7;
		for(ll i=1;i<=7;i++){
			if(sum>=n){
				day+=(i-1);
				break;
			}
			if(i==6||i==7)sum+=b;
			else sum+=a;
		}
	}
	cout<<day;
	return 0;
}

(非AC代码) 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	ll a,b,n;cin>>a>>b>>n;
	vector<ll> num;
	num.push_back(0);
	ll cur=0;
	while(1){
		if(cur>=n)break;
		cur+=(5*a+2*b);
		num.push_back(cur);
	}
	ll l=1,r=num.size();
	ll mid=0;
	while(l<=r){
		mid=(l+r)/2;
		if(num[mid]>n)r=mid-1;
		else l=mid+1;
	}
	//cout<<l;
	
	if(num[l]==n)cout<<l*7;
	else{
		ll sum=(l-1)*(5*a+2*b);
		for(ll i=1;i<=7;i++){
			if(sum>=n){
				cout<<(l-1)*7+(i-1);
				break;
			}
			if(i==6||i==7)sum+=b;
			else sum+=a;
		}
	}
	return 0;
}

。。。。。 

D.修剪灌木 

 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	ll n;cin>>n;
	if(n==1){
		cout<<1;
		return 0;
	}
	vector<ll> a(n+1,0);
	if(n&1){
		a[n/2+1]=n-1;
		ll l=n/2;
		ll r=n/2+2;
		ll cnt=n-1+2;
		while(l>=1&&r<=n){
			a[l]=a[r]=cnt;
			cnt+=2;
			l--;r++;
		}
	}else{
		ll l=n/2;
		ll r=n/2+1;
		ll cnt=n;
		while(l>=1&&r<=n){
			a[l]=a[r]=cnt;
			cnt+=2;
			l--;r++;
		}
	}
	for(ll i=1;i<=n;i++){
		cout<<a[i]<<'\n';
	}
	return 0;
}

纸老虎,图一画,一目了然,规律很好找。

E.X进制减法 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,ma,mb; 
int a[1000000],b[1000000];
ll sum[1000000];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin>>n;
    cin>>ma;
    for(int i=ma-1;i>=0;i--)     cin>>a[i];
    cin>>mb;
    for(int i=mb-1;i>=0;i--)     cin>>b[i];
    sum[0]=1;
    for(int i=1;i<ma;i++)
    {
        sum[i]=sum[i-1]*max(max(a[i-1]+1,b[i-1]+1),2);
        sum[i]%=1000000007;
    }
    ll absum=0;
    for(int i=0;i<ma;i++)
    {
        absum+=sum[i]*(a[i]-b[i]); 
        absum%=1000000007;
    }
    cout<<absum;
    return 0;
}

F.统计子矩阵 

内层循环优化一下就好了,首先想到的是双指针 ,OK开干!

​
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	ll n,m,k;cin>>n>>m>>k;
	ll mp[n+1][m+1]={0};
	ll s[n+1][m+1]={0};
	for(ll i=1;i<=n;i++){
		for(ll j=1;j<=m;j++){
			cin>>mp[i][j];
			s[i][j]=s[i][j-1]+s[i-1][j]-s[i-1][j-1]+mp[i][j];
			//s[i][j]=s[i-1][j];
		}
	}
	
	//for(ll i=1;i<=n;i++)for(ll j=1;j<=m;j++)cout<<s[i][j]<<'\n';
	ll count=0;
	
	for(int x1=1; x1<=n; x1++){
		
		for(int x2=x1; x2<=n; x2++){
			
			int y1=1,y2=1;
			
			while(y1<=y2&&y2<=m){
				
				while(s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y1-1]>k){
					
					y1++;
					
				}
				
				if(y1<=y2)count+=y2-y1+1;
				
				y2++;
				
			}
			
		}
		
	}
	cout<<count;
	return 0;
}

​

 

  • 通过前缀和的更新 s[i][j] += s[i-1][j],计算出每一列的前缀和,使得 s[i][j] 存储的是从 (1, j)(i, j) 的列和。

需要注意的是,这里使用了一维前缀和,只对列进行累加。横向的前缀和在后续处理中会利用双指针进行维护。

3. 外层两个循环:枚举矩阵的上下边界

  • 外层两个循环 ij 分别枚举所有可能的 上下边界。具体来说,i 是矩阵的上边界,j 是矩阵的下边界。对于每一对上下边界 (i, j),我们会计算这个上下边界所限定的所有子矩阵的和。

4. 内层循环:枚举矩阵的左右边界

  • leftright 是双指针,分别表示当前子矩阵的 左边界右边界
    • sum 是当前矩阵的和(列方向的和,已使用前缀和进行累加)。
    • right 从 1 遍历到 m,表示当前子矩阵的右边界。每次右移时,都要更新当前矩阵的和。
    • 如果 sum 超过了 k,就通过增加 left 来缩小子矩阵的宽度,直到 sum <= k

G.积木画 

 

#include<bits/stdc++.h>
const int N=10000005,m=1000000007;
int n,f[N];
using namespace std;
int main()
{
	scanf("%d",&n);
	f[0]=1;f[1]=1;f[2]=2;f[3]=5;
	for(int i=4;i<=n;i++)
		f[i]=(((f[i-1]+(f[i-2]*2)%m)%m+f[i-3])%m+f[i-4])%m;
	printf("%d",f[n]%m);
	return 0;
}

 

#include <stdio.h>
const int mod = 1e9+7,N = 1e7;
int main()
{
    int f[N]={0,1,2,5};
    int n;
    scanf("%d",&n);
    for(int i=4;i<=n;++i)
        f[i] = (2*f[i-1]%mod+f[i-3]%mod)%mod;
    printf("%d",f[n]);
    return 0;
}

 H.扫雷 

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int M = 999997;
const int Base = 1e9 + 1;
int res;
LL hash1[M]; //hash1用于维护find函数
int hash2[M],hash3[M],book[M]; //hash2:地雷在某点的出现次数 hash3:某点的地雷半径 book:标记该点是否被搜索
//Base进制编码降维
LL get_key(int x,int y)
{
    return (LL)x * Base + y;
}
//手写哈希函数
int find(LL x)
{
    int t = (x % M + M) % M;
    while(hash1[t]!=-1&&hash1[t]!=x)
    {
        t++;
        if(t==M) t=0;
    }
    return t;
}
//判断(x2,y2)是否位于(x1,y1,r1)的范围内
bool judge(int x1,int y1,int x2,int y2,int r)
{
    int d = (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1);
    return d <= r*r;
}
//dfs
void dfs(int x,int y,int r)
{
    for(int i=-r;i<=r;i++)
        for(int j=-r;j<=r;j++)
        {
            int dx = x + i;
            int dy = y + j;
            LL t = get_key(dx,dy);
            if(hash2[find(t)]&&judge(x,y,dx,dy,r)&&!book[find(t)])
            {
                res += hash2[find(t)]; //答案加上该点地雷个数
                book[find(t)] = 1; //标记为已搜索
                dfs(dx,dy,hash3[find(t)]); //搜索下一个点
            }
        }
    return;
}
int main()
{
    int n,m;
    cin>>n>>m;
    memset(hash1,-1,sizeof hash1); //hash1初始化为空
    for(int i=0;i<n;i++)
    {
        int x,y,r;
        cin>>x>>y>>r;
        LL t = get_key(x,y);
        hash1[find(t)] = t; //维护哈希函数
        hash2[find(t)]++; //统计该点地雷数量
        hash3[find(t)] = max(r,hash3[find(t)]); //记录该点地雷半径的最大值
    }
    for(int i=0;i<m;i++)
    {
        int x,y,r;
        cin>>x>>y>>r;
        dfs(x,y,r); //从每一个排雷火箭引爆点开始dfs
    }
    cout<<res<<endl;
    return 0;
}

暴力的将排雷火箭和爆炸的地雷装入队列对余下的地雷进行检验是否能引爆 我使用了一点小优化,在装地雷的数组中,将引爆的地雷放到数组的前面,下次遍历地雷数组时就可以只遍历还没引爆的地雷了 注意使用较快的输入和输出 参考代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct node{
	ll x,y,r;
};
bool check(node a,node b){
	return a.r*a.r>=((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
	//return ((pow(a.x-b.x,2)+pow(a.y-b.y,2))<=pow(a.r,2));
}
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	ll n,m;cin>>n>>m;
	//scanf("%lld %lld",&n,&m);
	node a[n+1];
	for(ll i=1;i<=n;i++)
		//scanf("%lld%lld%lld",&a[i].x,&a[i].y,&a[i].r);
		cin>>a[i].x>>a[i].y>>a[i].r;
	
	queue<node> q;
	for(ll i=0;i<m;i++){
		node t;
		//scanf("%lld%lld%lld",&t.x,&t.y,&t.r);
		cin>>t.x>>t.y>>t.r;
		q.push(t);
	}
	
	ll k=1;
	ll ans=0;
	while(!q.empty()){
		node t=q.front();q.pop();
		for(ll i=k;i<=n;i++){
			if(check(t,a[i])){
				q.push(a[i]);
				swap(a[k],a[i]);//交换到最前面
				k++;//下次遍历就可以跳过爆炸的地雷
				ans++;
				if(ans==n){
					cout<<ans;
					//printf("%lld",ans);
					return 0;
				}
			}
		}
	}
	cout<<ans;
	//printf("%lld",ans);
	return 0;
}

I.李白打酒加强版 

 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=1000000007;
ll vis[101][101][101];
// ll vis[101][101][101]={{{-1}}};仍然是错误的
ll dfs(ll n,ll m,ll sum){
	if(n>=m)return 0;//剩下的酒喝不完了,还再加酒
	if(sum==0)return (n==0&&m==0);//酒喝完了也走到头了
	if(n==0) return (sum==m);//剩下的酒必须得能消耗完
	if(sum==0&&n==0&&m!=0)return 0;//没有酒了,还碰到花了
	if(sum>m) return 0;//酒喝不完了
	if(vis[n][m][sum]!=-1)return vis[n][m][sum];
	else{
		ll ans=dfs(n-1,m,sum*2)+dfs(n,m-1,sum-1);
		ans%=N;
		vis[n][m][sum]=ans;
		return ans;
	}
}
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	ll n,m;cin>>n>>m;
	memset(vis, -1, sizeof vis);
	cout<<dfs(n,m,2);
	return 0;
}

 

#include<stdio.h>
#include<string.h>
#define mod 1000000007
#define max 105
int dp[max][max][max];
int main()
{
    int N,M,flag=0,i,j,k;
    scanf("%d%d",&N,&M);
    memset(dp,0,sizeof(dp));
    dp[N][M][2]=1;            //初始化刚开始手里面是有2斗酒
    for(i=N; i>=0; i--)
    {
        for (j = M; j>=0; j--)
        {
            for (k = M; k>=0; k--)
            {
                if(2*k<=M&&i>=1)
                {
                    dp[i-1][j][k<<1]=(dp[i][j][k]+dp[i-1][j][k<<1])%mod;
                }
                if(j>=1&&k>=1)
                {
                    dp[i][j-1][k-1]=(dp[i][j-1][k-1]+dp[i][j][k])%mod; 
                }
            }
        }
    }
    printf("%d",dp[0][1][1]%mod);
    return 0;
}

J. 砍竹子

 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;  // 定义 long long 类型为 ll,便于处理大数
const int N = 2e5 + 5;  // 设置最大竹子数量 n 的大小为 2e5 + 5(为防止数组越界)
int n;  // 竹子数量
ll a[N][10];  // 用二维数组存储每棵竹子在每次魔法操作后的状态

int main() {
  cin >> n;  // 输入竹子数量
  ll s[10];  // 用于存储每棵竹子从原始高度经过每次魔法操作后的结果
  int res = 0, maxstep = 0;  // res 用来记录最少的魔法次数,maxstep 用来记录最大的操作步骤数

  for (int i = 0; i < n; i++) {  // 对每棵竹子进行处理
    ll x;  // 当前竹子的高度
    cin >> x;  // 输入当前竹子的高度
    int top = 0;  // 用 top 记录当前竹子从初始高度到1所需要的操作步骤数

    // 循环模拟竹子的高度变化过程,每次将高度减少到当前高度的一半并向下取整
    while (x > 1) {
      s[++top] = x;  // 记录每一步的高度
      x = sqrt(x / 2 + 1);  // 当前高度的魔法操作(向下取整后除以2再加1的平方根)
    }

    res += top;  // 将当前竹子需要的操作步骤数累加到 res
    maxstep = max(maxstep, top);  // 更新最大的操作步骤数

    // 将每一步的高度保存在 a 数组中,a[i] 保存第 i 棵竹子的每一层操作结果
    for (int j = 0, k = top; k > 0; j++, k--) {
      a[i][j] = s[k];  // 存储每一层操作的高度
    }
  }

  // 遍历所有操作步骤层,优化相邻相同高度的竹子,减少操作次数
  for (int i = 0; i < maxstep; i++) {  // 同层操作
    for (int j = 1; j < n; j++) {  // 从第二棵竹子开始,比较相邻竹子的操作状态
      if (a[j][i] && a[j - 1][i] == a[j][i]) {  // 如果相邻竹子的高度相同
        res--;  // 减少一次操作,因为这两棵竹子的操作可以合并
      }
    }
  }

  cout << res;  // 输出最少的魔法操作次数
  return 0;
}
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=2*1e5+5;
ll tree[N][10];
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	ll n;cin>>n;
	ll res=0;
	ll maxstep=0;
	ll s[10];
	for(ll i=0;i<n;i++){
		ll x;cin>>x;
		ll step=0;
		while(x>1){
			s[++step]=x;
			x=sqrt((x/2)+1);
		}
		maxstep=max(maxstep,step);
		res+=step;
		for(ll j=0,k=step;k>0;j++,k--){
			tree[i][j]=s[k];
		}
	}
	for(ll j=0;j<maxstep;j++){
		for(ll i=1;i<n;i++){
			if(tree[i][j]&&tree[i][j]==tree[i-1][j]){
				res--;
			}
		}
	}
	cout<<res;
	return 0;
}

 

 

tree[i][j] && tree[i][j] == tree[i-1][j] 中的 tree[i][j] 用作一个有效性检查,确保该高度值不是零。因为在某些情况下,tree[i][j] 可能是 0(例如,当竹子高度变成 1 时,可能没有后续的魔法操作),这种情况下不需要继续比较。

2. 没有对数组 a[i] 进行清空操作:

  • a[i] 在每次处理新竹子时是没有被清空的,这会导致如果前一次的竹子处理时的 a[i] 数组有值,可能会干扰到下一次的计算。

解决方法: 每次处理一个竹子时,确保清空当前 a[i] 数组的内容。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值