第十三届蓝桥杯C++B组省赛题解+复盘总结

文章目录

  • 1、九进制转十进制
  • 2、顺子日期
  • 3、刷题统计
  • 4、修剪灌木
  • 5、X 进制减法
  • 6、统计子矩阵
  • 7、积木画
  • 8、扫雷
  • 9、李白打酒加强版
  • 10、砍竹子

第十三届蓝桥杯C++B组省赛题解+复盘总结_第1张图片
国赛前的最后模拟,做的还行,没有特别难的题目,比较满意的是这次自己做的都对了,没有出现不必要的失误,10砍竹子补完感觉也挺可惜的。

1、九进制转十进制

2 ∗ 9 0 + 2 ∗ 9 1 + 0 ∗ 9 2 + 2 ∗ 9 3 = 1478 2*9^0+2*9^1+0*9^2+2*9^3=1478 290+291+092+293=1478

2、顺子日期

暴力枚举
答案:14

#include
#define LL long long
using namespace std;

typedef pair<int,int> PII;
int mon[]={0,31,28,31,30,31,30,31,31,30,31,30,31};
void solve(){
    int y=2022;
    if((y%4==0 && y%100!=0) || y%400==0) mon[2]++;
    string s="2022";
    int ans=0;
    for(int m=1;m<=12;m++){
        for(int d=1;d<=mon[m];d++){
            string t=s;
            int a=0,b=0;
            int x=m;
            a=x%10;
            x/=10;
            b=x;
            t+=('0'+b);
            t+=('0'+a);
            x=d;
            a=x%10;
            x/=10;
            b=x;
            t+=('0'+b);
            t+=('0'+a);
            for(int i=0;i+2<8;i++){
                if(t[i]==t[i+1]-1 && t[i+1]==t[i+2]-1){
//                    cout<
                    ans++;
                    break;
                }
            }
        }
    }
    cout<<ans<<'\n';
//    cout<<14<<'\n';
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int T=1;
//    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

3、刷题统计

一开始想二分天数,但是范围1e18,会爆longlong
多想了下,可以直接利用除法求出周数,剩下的天数枚举判断一下
还好多想了下,不然写二分肯定寄

#include
#define int long long
using namespace std;

typedef pair<int,int> PII;

void solve(){
    int a,b,n;
    cin>>a>>b>>n;
    int ww=n/(5*a+2*b);
    int rr=n%(5*a+2*b);
    int ans=ww*7;
    int x=0;
    for(int i=1;i<=7;i++){
        if(x>=rr){
            ans+=i-1;
            break;
        }
        if(x<rr){
            if(i<=5) x+=a;
            else x+=b;
        }
        if(x>=rr){
            ans+=i;
            break;
        }
    }
    cout<<ans<<'\n';
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int T=1;
//    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

4、修剪灌木

直觉就是 m a x ( i − 1 , n − i ) ∗ 2 max(i-1,n-i)*2 max(i1,ni)2
手玩几个样例也能发现

#include
#define LL long long
using namespace std;

typedef pair<int,int> PII;

void solve(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cout<<max(i-1,n-i)*2<<'\n';
    }
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int T=1;
//    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

5、X 进制减法

这题一开始没懂题意说的321转为十进制数为65是咋搞的,做完后面的题再来思考有了新的发现
65 / 2 = 32.....1 , 32 / 10 = 3.....2 , 3 / 8 = 0....3 65/2=32.....1,32/10=3.....2,3/8=0....3 65/2=32.....132/10=3.....23/8=0....3余数依次为321,类比一下十进制数转二进制的方法
所以计算方法是 65 = ( 3 ∗ 10 + 2 ) ∗ 2 + 1 65=(3*10+2)*2+1 65=(310+2)2+1
再看样例最高位11进制,第二位为5进制,最低位为2进制,对于所给10,4,0求转为十进制后的数为:
( 10 ∗ 5 + 4 ) ∗ 2 + 0 = 108 (10*5+4)*2+0=108 (105+4)2+0=108
让A-B最小,贪心的想就是让进制数越小越好,再考虑合法性,所以有:
c [ i ] = m a x ( a [ i ] + 1 , b [ i ] + 1 , 2 ) c[i]=max(a[i]+1,b[i]+1,2) c[i]=max(a[i]+1,b[i]+1,2)
详情看代码:

#include
#define int long long
using namespace std;

typedef pair<int,int> PII;
const int N=1e5+10,mod=1e9+7;
int ma,mb,a[N],b[N],n,c[N];
void solve(){
    cin>>n;
    cin>>ma;
    for(int i=1;i<=ma;i++) cin>>a[i];
    cin>>mb;
    for(int i=ma-mb+1;i<=ma;i++) cin>>b[i];
    for(int i=1;i<=ma;i++){
        c[i]=max(a[i],b[i])+1;
        c[i]=max(c[i],2ll);
    }
    int x1=a[1];
    for(int i=2;i<=ma;i++){
        x1=((x1*c[i]%mod)+a[i])%mod;
    }
    int x2=b[1];
    for(int i=2;i<=ma;i++){
        x2=((x2*c[i]%mod)+b[i])%mod;
    }
    int ans=(x1-x2+mod)%mod;
    cout<<ans<<'\n';
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int T=1;
//    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

6、统计子矩阵

二位前缀和+二分
枚举矩阵的左上角,然后枚举高,二分最大的宽
这题写的慢了,二位前缀和一开始写错了,耽误了时间

#include
#define LL long long
using namespace std;

typedef pair<int,int> PII;
const int N=510;
int a[N][N],s[N][N];
int n,m,k;
int get(int x1,int y1,int x2,int y2){
    return s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1];
}
void solve(){
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>a[i][j];
            s[i][j]=a[i][j];
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
        }
    }
    LL ans=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            for(int d=1;d<=n-i+1;d++){
                int x=i+d-1;
                if(get(i,j,x,j)>k) continue;
                int l=j,r=m;
                while(l<=r){
                    int mid=l+r>>1;
                    if(get(i,j,x,mid)<=k) l=mid+1;
                    else r=mid-1;
                }
                int res=l-1-j+1;
                ans+=(LL)res;
            }
        }
    }
    cout<<ans<<'\n';
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int T=1;
//    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

7、积木画

用0表示没填,1表示填了,每一列我们考虑三种状态,11,01,10
定义 d p i , 0 / 1 / 2 为前 i − 1 列填满,第 i 列的状态为 11 , 01 , 10 时的方案数 dp_{i,0/1/2}为前i-1列填满,第i列的状态为11,01,10时的方案数 dpi,0/1/2为前i1列填满,第i列的状态为11,01,10时的方案数
转移:
d p i , 0 = d p i − 1 , 0 (竖放一个 I 形积木) + d p i − 1 , 1 (放一个 L 形积木) + d p i − 1 , 2 (放一个 L 形积木) + d p i − 2 , 0 (横放两个 I 形积木) dp_{i,0}=dp_{i-1,0}(竖放一个I形积木)+dp_{i-1,1}(放一个L形积木)+dp_{i-1,2}(放一个L形积木)+dp_{i-2,0}(横放两个I形积木) dpi,0=dpi1,0(竖放一个I形积木)+dpi1,1(放一个L形积木)+dpi1,2(放一个L形积木)+dpi2,0(横放两个I形积木)
注意这里可以通过横放两个 I I I积木转移,赛时一开始没想到,样例过不去,好在后面的做完再来看的时候想到了
其他状态转移类似,详情看代码

#include
#define int long long
using namespace std;

typedef pair<int,int> PII;
const int N=1e7+10,mod=1e9+7;
int dp[N][3];
void solve(){
    int n;
    cin>>n;
    dp[0][0]=1;
    for(int i=1;i<=n;i++){
        dp[i][0]=(((dp[i-1][0]+dp[i-1][1])%mod)+dp[i-1][2])%mod;
        dp[i][1]=dp[i-1][2];
        dp[i][2]=dp[i-1][1];
        if(i-2>=0){
            dp[i][1]=(dp[i][1]+dp[i-2][0])%mod;
            dp[i][2]=(dp[i][2]+dp[i-2][0])%mod;
            dp[i][0]=(dp[i][0]+dp[i-2][0])%mod;
        }
    }
    cout<<dp[n][0]<<'\n';
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int T=1;
//    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

8、扫雷

蓝桥云课的纯送分题,与当年实际题目不符

#include
#define LL long long
using namespace std;

typedef pair<int,int> PII;
const int dir[][2]={{-1,0},{1,0},{0,1},{0,-1},{1,1},{1,-1},{-1,1},{-1,-1}};
void solve(){
    int n,m;
    cin>>n>>m;
    vector<vector<int>> g(n+1,vector<int>(m+1));
    vector<vector<int>> ans(n+1,vector<int>(m+1));
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>g[i][j];
            if(g[i][j]){
                ans[i][j]=9;
            }
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(g[i][j]) continue;
            for(int k=0;k<8;k++){
                int x=i+dir[k][0];
                int y=j+dir[k][1];
                if(x>=1 && x<=n && y>=1 && y<=m && g[x][y]) ans[i][j]++;
            }
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cout<<ans[i][j]<<' ';
        }
        cout<<'\n';
    }
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int T=1;
//    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

9、李白打酒加强版

当酒斗数大于m时,最后就不可能喝光为0
定义 d p i , j , k 为遇到店 i 次,遇到花 j 次,当前酒斗数为 k 的方案数 dp_{i,j,k}为遇到店i次,遇到花j次,当前酒斗数为k的方案数 dpi,j,k为遇到店i次,遇到花j次,当前酒斗数为k的方案数
转移:
d p i , j , k = d p i − 1 , j , k / 2 + d p i , j − 1 , k + 1 dp_{i,j,k}=dp_{i-1,j,k/2}+dp_{i,j-1,k+1} dpi,j,k=dpi1,j,k/2+dpi,j1,k+1
注意判断边界,详情看代码:

#include
#define int long long
using namespace std;

typedef pair<int,int> PII;
const int N=105,mod=1e9+7;
int dp[N][N][N];
int n,m;
void solve(){
    cin>>n>>m;
    dp[0][0][2]=1;
    for(int i=0;i<=n;i++){
        for(int j=0;j<=m;j++){
            for(int k=0;k<=m+1;k++){
                if(k%2==0 && i-1>=0) dp[i][j][k]=(dp[i][j][k]+dp[i-1][j][k/2])%mod;
                if(j-1>=0 && k+1<=m+1) dp[i][j][k]=(dp[i][j][k]+dp[i][j-1][k+1])%mod;
            }
        }
    }
    cout<<dp[n][m-1][1]<<'\n';
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int T=1;
//    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

10、砍竹子

范围1e18最多6次就可以变为1
直觉就是用优先队列每次对最大数进行操作
但是当时没想好怎么处理最大值相邻且相等的时候如何处理相邻数的问题,再加上没剩多少时间,前面还有题卡着,有点着急,就先写了暴力
赛后补了,其实只需要让每次弹出的最大值的下标是最左边的就行,然后不断向右操作。(需要满足与最大值相等)
注意sqrt不能用于特别大的数字,得使用sqrtl,害人不浅

#include
#define int long long
using namespace std;

typedef pair<int,int> PII;
const int N=2e5+10;
int n,a[N];
struct node{
    int v,id;
    bool operator<(const node&t)const{
        if(v==t.v) return id>t.id;
        return v<t.v;
    }
};
void solve(){
    cin>>n;
    priority_queue<node> q;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        q.push({a[i],i});
    }
    int ans=0;
    while(!q.empty()){
        auto [v,id]=q.top();
        q.pop();
        if(v<=1) break;
        int mxv=v,pos=id;
        int x=sqrtl(v/2+1);
        q.push({x,id});
        while(!q.empty() && q.top().v==mxv && q.top().id==pos+1){
            q.pop();
            q.push({x,pos+1});
            pos++;
        }
        ans++;
    }
    cout<<ans<<'\n';
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int T=1;
//    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

你可能感兴趣的:(蓝桥杯,蓝桥杯,c++,职场和发展)