2018年第九届蓝桥杯省赛C语言B组题解

2018年的题感觉比前两年的难上一些,之前是有想法但错,这次是完全没想法,毕竟这次好像没dfs了。

1.第几天

手算或者打开Excel用函数
答案:125

2.明码

把对应的像素点标记为1,输出时有1就输出星号,没就输出空格。最后像素点问的是:“九的九次方等于多少?"
pow一下就好
答案:387420489

d
#include <iostream>
#include <cstring>
using namespace std;
int ziji[8];
int main()
{
    int a;
    for(int i=1;i<=10*32;i++)
    {
        memset(ziji,0,sizeof(ziji));
        cin>>a;
        for(int j=7;j>=0;j--)
        {
            ziji[j]=a&1;
            a>>=1;
        }
        for(int j=0;j<=7;j++)
            if(ziji[j]==1)
                cout<<"*";
            else cout<<" ";
        if(i%2==0)
            cout<<endl;
    }
    return 0;
} 

3.乘积末尾有多少个零

0的来源是2×5。所以对每个数都不停地取2和5的因子,最后两者的较小值就是答案。
答案:31

#include <iostream>
#include <algorithm>
using namespace std;
int infor[10][10],all,ans[2];
int main()
{
    int i,j;
    for(i=0;i<10;i++)
       for(j=0;j<10;j++)
           cin>>infor[i][j];
    for(i=0;i<10;i++)
        for(j=0;j<10;j++)
            while(infor[i][j]%2==0)//取2
            {
                infor[i][j]/=2;
                ans[0]++;
            }
    for(i=0;i<10;i++)
        for(j=0;j<10;j++)   
            while(infor[i][j]%5==0)//取5
            {
                infor[i][j]/=5;
                ans[1]++;
            }
    all=min(ans[0],ans[1]);
    cout<<all;
    return 0;
}

4.测试次数

标题:测试次数
x星球的居民脾气不太好,但好在他们生气的时候唯一的异常举动是:摔手机。
各大厂商也就纷纷推出各种耐摔型手机。x星球的质监局规定了手机必须经过耐摔测试,并且评定出一个耐摔指数来,之后才允许上市流通。
x星球有很多高耸入云的高塔,刚好可以用来做耐摔测试。塔的每一层高度都是一样的,与地球上稍有不同的是,他们的第一层不是地面,而是相当于我们的2楼。
如果手机从第7层扔下去没摔坏,但第8层摔坏了,则手机耐摔指数=7。
特别地,如果手机从第1层扔下去就坏了,则耐摔指数=0。
如果到了塔的最高层第n层扔没摔坏,则耐摔指数=n
为了减少测试次数,从每个厂家抽样3部手机参加测试。
某次测试的塔高为1000层,如果我们总是采用最佳策略,在最坏的运气下最多需要测试多少次才能确定手机的耐摔指数呢?
请填写这个最多测试次数。
注意:需要填写的是一个整数,不要填写任何多余内容。
答案:19

一开始想二分,突然发现限制了手机数,玩完,不会。后来看题解,是动态规划,想不到,在这里就做一次解释并思考用动态规划的原因。
解:
假设i个手机共j层楼,如果在k层摔坏了,那么就是i-1个手机k-1层楼的情况;如果没坏,就是i个手机测k+1到j层的情况,此情况与i个手机测1到j-k层的情况等价(假设把层数减k,把抗摔能力也减k,消减并不会对结果产生影响,所以等价)。得出递推式:dp[i][j]=max(dp[i-1][k-1],dp[i][j-k])+1,k ∈ \in [1,j-1](相当于把上述过程逆推)用max是因为要使得dp[i][j]满足所有情况,再做更新取最小:dp[i][j]=min(dp[i][j],max(dp[i-1][k-1],dp[i][j-k])+1)),k ∈ \in [1,j-1]。
一开始初始化为最坏情况,j层楼为j次。

#include <iostream>
#include <algorithm>
using namespace std;
int dp[4][1010],i,j,k;
int main()
{
    for(i=1;i<=3;i++)
        for(j=1;j<=1000;j++)
            dp[i][j]=j;//所有的最坏情况 
    for(i=2;i<=3;i++)
        for(j=1;j<=1000;j++)
            for(k=1;k<j;k++)
                dp[i][j]=min(dp[i][j],max(dp[i-1][k-1],dp[i][j-k])+1);
    cout<<dp[3][1000];
    return 0;
} 

代码十分简洁。
转一下动态规划问题的特点:
此类问题具有的特点:
【1】问题的目标是求一个问题的最优解;
【2】整体问题的最优解依赖于各个子问题的最优解;
【3】把大问题分解成若干个小问题,这些小问题之间还有相互重叠的更小的子问题;
【4】从上往下分析问题,从下往上解决问题。

5.快速排序

又烂了
答案:a,i+1,r,k-(i-l+1)

6.递增三元组

**思路:**要找满足Ai < Bj < Ck的数据,显然暴力是过不去的 。那么关于搜索,可以用二分来实现,而二分要求有序,即要先排序,sort实现。,具体写法是以为B中心,向A和C搜索,分别搜索小于此时的B和大于此时B的值的数量

#include <iostream>
#include <algorithm>
#define MAX 100005
using namespace std;
int a[MAX],b[MAX],c[MAX];
int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
        cin>>a[i];
    for(int i=0;i<n;i++)
        cin>>b[i];
    for(int i=0;i<n;i++)
        cin>>c[i];
    sort(a,a+n);
    sort(b,b+n);
    sort(c,c+n);//排序用于二分
    int sum=0;
    for(int i=0;i<n;i++)
    {
        int x=(lower_bound(a,a+n,b[i])-a); //大于等于此值的第一个,向前共多少个       
        int y=n-(upper_bound(c,c+n,b[i])-c); //大于此值的第一个,向后共多少个
        sum+=x*y;//累计
    }
    cout<<sum<<endl;
    return 0;
}

7.dis(x,y)

看似搜索,但是会爆,实则分块找规律,分块一般是靠y=x和y=-x两条线来分割,分割点要仔细考虑,其余的就是列表找规律了。

#include <iostream>
#include <cmath>
using namespace std;
int x,y;
int main()
{
    cin>>x>>y;
    if(y>=0&&abs(x)<=y) cout<<4*y*y+x-y;
    else if(y<0&&abs(x)<=abs(y)) cout<<(-2*y+1)*(-2*y+1)-x+y-1;
    else if(x>0&&abs(y)<x) cout<<4*x*x+x-y;
    else if(x<0&&abs(y)<abs(x)) cout<<(-2*x-1)*(-2*x-1)-x+y-1; 
    return 0;
} 

8.日志统计

小明维护着一个程序员论坛。现在他收集了一份”点赞”日志,日志共有N行。其中每一行的格式是:
ts id
表示在ts时刻编号id的帖子收到一个"赞"。
现在小明想统计有哪些帖子曾经是"热帖"。如果一个帖子曾在任意一个长度为D的时间段内收到不少于K个赞,小明就认为这个帖子曾是"热帖"。
具体来说,如果存在某个时刻T满足该帖在[T, T+D)这段时间内(注意是左闭右开区间)收到不少于K个赞,该帖就曾是"热帖"。
给定日志,请你帮助小明统计出所有曾是"热帖"的帖子编号。
【输入格式】
第一行包含三个整数N、D和K。
以下N行每行一条日志,包含两个整数ts和id。
对于50%的数据,1 <= K <= N <= 1000
对于100%的数据,1 <= K <= N <= 100000 0 <= ts <= 100000 0 <= id <= 100000
【输出格式】
按从小到大的顺序输出热帖id。每个id一行。
【输入样例】
7 10 2
0 1
0 10
10 10
10 1
9 1
100 3
100 3
【输出样例】
1
3
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms

解:
对每个id分别记录,防止消耗内存过大用vector,再对每个id分别判断,用尺取法(记录区间左右端点,不断推进右端点,满足条件时移动左端点,不满足再移动右端点,直到右端点到极限),不然应该会爆吧。

#include <iostream>
#include <vector>
#include <algorithm>
#define MAXN 100010 
using namespace std;
int ans[MAXN],n,d,k,ts,id,all;
vector<int> infor[MAXN];
bool check(int a)
{
    int i,sum=0,l=0,r=0,len=infor[a].size();
    if(len<k) return false;
    sort(infor[a].begin(),infor[a].end());
    while(l<=r&&r<len)//尺取法
    {
        sum++;
        if(sum>=k)
        {    
             if(infor[a][r]-infor[a][l]<d)
             return true;
             else
             {
                l++;
                sum--;
             }
       }
       r++;
    }
    return false;
}
int main()
{
    int i;
    cin>>n>>d>>k;
    for(i=0;i<n;i++)
    {
        cin>>ts>>id;
        infor[id].push_back(ts);
    }
    for(i=1;i<=10000;i++)
        if(check(i))
            ans[all++]=i;
    for(i=0;i<all;i++)
        cout<<ans[i]<<endl;
    return 0;
}

9.全球变暖

dfs完事

#include <iostream>
using namespace std;
char t;
int n,lef,all,dir[4][2]={1,0,-1,0,0,1,0,-1};
bool use1[1010][1010],use2[1010][1010],use3[1010][1010];
void dfs(int a,int b,bool (*c)[1010],bool (*d)[1010])//标记
{
    d[a][b]=false;
    int x,y,i;
    for(i=0;i<4;i++)
    {
        x=a+dir[i][0];
        y=b+dir[i][1];
        if(x>=0&&x<n&&y>=0&&y<n&&c[x][y]&&d[x][y])
            dfs(x,y,c,d);
    }
}
bool check(int a,int b)//淹水
{
    for(int i=0;i<4;i++)
    {
        int x=a+dir[i][0],y=b+dir[i][1];
        if(!use1[x][y])
            return false; 
    }
    return true;
}
int main() 
{
    int i,j;
    cin>>n;
    for(i=0;i<n;i++)
        for(j=0;j<n;j++)
        {
            cin>>t;
            if(t=='#')
                use1[i][j]=true;
            use3[i][j]=use2[i][j]=use1[i][j];
        }
    for(i=0;i<n;i++)
        for(j=0;j<n;j++)
            if(use1[i][j]&&use2[i][j])
            {
                all++;//搜原有多少岛
                dfs(i,j,use1,use2); //标记此岛
            }
    for(i=0;i<n;i++)
        for(j=0;j<n;j++)
            if(use/1[i][j])
                use3[i][j]=check(i,j);//开淹
    for(i=0;i<n;i++)
        for(j=0;j<n;j++)
            if(use3[i][j])
            {
                lef++;//剩余
                dfs(i,j,use3,use3);//标记此岛
            }
    cout<<all-lef;//消失的数量
    return 0;
}

10.乘积最大

**思路:**按绝对值大小排序:
1.如果前k个里有0,即后面都是0,那么只能取乘积最大只能为0;
2.如果前k个里负数的个数为偶数个,那就取前k个;
3.如果负数的个数为奇数个:
可以把前k个里最小的负数换成后面最大的正数或者把前k个里最小的正数换成后面最大的负数,比较哪种大,即可。
若其中一种不存在,直接取另一种;
若都凑不齐,看看后面有没有0,有0最大就是0,若没有,从后往前取k个,此时乘积是最大的负数。

#include <iostream>
#include <cmath>
#include <algorithm>
#define MAXN 1000000009
using namespace std;
typedef long long int ll;
ll infor[100010],n,k,z,f,o,sum=1;
bool comp(int a,int b)//按绝对值由大到小排序
{
    return abs(a)>abs(b);
}
int main()
{
    int i,j;
    cin>>n>>k;
    for(i=0;i<n;i++)
        cin>>infor[i]; 
    sort(infor,infor+n,comp);//按绝对值从大到小排
    for(i=0;i<k;i++)
    {
        if(infor[i]>0) z++;//正的数
        else if(infor[i]<0) f++;//负的
        else o++;//0
    }
    if(o) sum=0;//前k个有0,只能是0
    else if(f%2==0) //偶数个负数,前k个全用就最大
    {
         for(i=0;i<k;i++)
         {
             sum*=infor[i];
             if(sum>0) sum%=MAXN;
             else sum=0-(0-sum)%MAXN;//取余
         }
    }
    else//奇数个负数
    {
        ll zxz=1000000,zxf=0,zdz=0,zdf=-1000000;//最小正数,最小负数,最大正数,最大负数(指绝对值)
        for(i=0;i<k;i++)//在前k个里取最小负数、最小正数
        {
            if(infor[i]>=0) zxz=min(zxz,infor[i]);
            else zxf=min(zxf,infor[i]);
        }
        for(i=k;i<n;i++)//在后续取最大正数、最大负数
        {
            if(infor[i]>=0) zdz=max(zdz,infor[i]);
            else zdf=max(zdf,infor[i]);
        }
        if((zdz==0||zxf==0)&&(zdf==-1000000||zxz==1000000))//都找不全没法换
        {
            int flag=0;
            for(i=0;i<n;i++)
                if(infor[i]==0)
                {
                    flag=1;
                    break;
                }
            if(flag) sum=0;//有0就为0
            else//没有0,反正为负,就取小的,负的少一点
            {
                for(i=n-1,j=0;j<k;i--,j++)
                {
                    sum*=infor[i];
                    if(sum>0) sum%=MAXN;
                    else sum=0-(0-sum)%MAXN;
                }
            }
        }
        else if((zdf==-1000000||zxz==1000000)||zdz*zxz>zdf*zxf)//没找到最小正数或最大负数或者替换最小负数时更大
        {
            int flag=0;
            for(i=0;i<k;i++)
            {
                if(!flag&&infor[i]==zxf)//最小负不算进去
                {
                    flag=1;
                    continue;
                } 
                sum*=infor[i];
                if(sum>0) sum%=MAXN;
                else sum=0-(0-sum)%MAXN;
            }  
            sum=sum*zdz%MAXN;
        }
        else if((zdz==0||zxf==0)||zdz*zxz<=zdf*zxf)//没找到最大正数或最小负数或替换最小正数时更大
        {
            int flag=0;
            for(i=0;i<k;i++)
            {
                if(!flag&&infor[i]==zxz)
                {
                    flag=1;
                    continue;
                }
                sum*=infor[i];
                if(sum>0) sum%=MAXN;
                else sum=0-(0-sum)%MAXN;
            }
            sum=sum*zdf%MAXN;
        }
    }
    cout<<sum<<endl;
    return 0;
} 
  • 5
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值