zoukankan      html  css  js  c++  java
  • 蓝桥杯第九届(2018)B组省赛3-10题练手源码

    3、求乘积末尾零个数

    【题目】
    如下的10行数据,每行有10个整数,请你求出它们的乘积的末尾有多少个零?

    5650 4542 3554 473 946 4114 3871 9073 90 4329
    2758 7949 6113 5659 5245 7432 3051 4434 6704 3594
    9937 1173 6866 3397 4759 7557 3070 2287 1453 9899
    1486 5722 3135 1170 4014 5510 5120 729 2880 9019
    2049 698 4582 4346 4427 646 9742 7340 1230 7683
    5693 7015 6887 7381 4172 4341 2909 2027 7355 5649
    6701 6645 1671 5978 2704 9926 295 3125 3878 6785
    2066 4247 4800 1578 6652 4616 1113 6205 3264 2915
    3966 5291 2904 1285 2193 1428 2265 8730 9436 7074
    689 5510 8243 6114 337 4096 8199 7313 3685 211

    注意:需要提交的是一个整数,表示末尾零的个数。

    【思路】
    看这些数当中2与5有多少对,将每一个数的因数中的2的个数统计出来,5同理,然后取两者的最小值即为对数。
    【题解】

    #include<cstdio>
    #include<cmath>
    #include<iostream>
    #include<algorithm>
    using namespace std;
     
    int main()
    {
        int num[100] = { 5650,4542,3554,473,946,4114,3871,9073,90,4329,
                        2758,7949,6113,5659,5245,7432,3051,4434,6704,3594,
                        9937,1173,6866,3397,4759,7557,3070,2287,1453,9899,
                        1486,5722,3135,1170,4014,5510,5120,729,2880,9019,
                        2049,698,4582,4346,4427,646,9742,7340,1230,7683,
                        5693,7015,6887,7381,4172,4341,2909,2027,7355,5649,
                        6701,6645,1671,5978,2704,9926,295,3125,3878,6785,
                        2066,4247,4800,1578,6652,4616,1113,6205,3264,2915,
                        3966,5291,2904,1285,2193,1428,2265,8730,9436,7074,
                        689,5510,8243,6114,337,4096,8199,7313,3685,211 };
        int two = 0, five = 0;
        for (int i = 0; i < 100; i++)
        {
            int t = num[i];
            while (t)
            {
                if (t % 2 == 0)
                {
                    t /= 2;
                    two++;
                }
                else
                    break;
            }
            t = num[i];
            while (t)
            {
                if (t % 5 == 0)
                {
                    t /= 5;
                    five++;
                }
                else
                    break;
            }
        }
        cout << min(two, five) << endl;
        return 0;
    }
    
    

    4、手机测试

    【题目】
    x星球的居民脾气不太好,但好在他们生气的时候唯一的异常举动是:摔手机。
    各大厂商也就纷纷推出各种耐摔型手机。x星球的质监局规定了手机必须经过耐摔测试,并且评定出一个耐摔指数来,之后才允许上市流通。
    x星球有很多高耸入云的高塔,刚好可以用来做耐摔测试。塔的每一层高度都是一样的,与地球上稍有不同的是,他们的第一层不是地面,
    而是相当于我们的2楼。
    如果手机从第7层扔下去没摔坏,但第8层摔坏了,则手机耐摔指数=7。
    特别地,如果手机从第1层扔下去就坏了,则耐摔指数=0。
    如果到了塔的最高层第n层扔没摔坏,则耐摔指数=n
    为了减少测试次数,从每个厂家抽样3部手机参加测试。
    某次测试的塔高为1000层,如果我们总是采用最佳策略,在最坏的运气下最多需要测试多少次才能确定手机的耐摔指数呢?
    请填写这个最多测试次数。
    注意:需要填写的是一个整数,不要填写任何多余内容。
    【思路】
    设这个测试次数为k次,3部手机一共测试k次可以测出耐摔指数。既然有k次机会,我们不妨就把第1部手机先从第k层楼摔下去。
    如果第1部手机摔死了,那么第2部手机剩下k-1次机会,可以从1~k-1层来测试。如果第1部手机没摔死,那么它还剩下k-1次机会,
    那我们下次就可以从第k+(k-1)层楼摔。如果它这一次摔死了,那么第2部手机还有k-2次机会,就可以从k+1~k+(k-1)-1层来测
    试。如果第1部手机在第k+(k-1)层摔下来后仍旧活下来了,那么它还有k-2次机会,在下次就可以从第k+(k-1)+(k-2)层摔。以
    此类推,一定可以在k次内测出耐摔指数。
    那么有人就要说了,你这里只说了2部手机,可是我们有3部手机啊。其实情况是一样的,假如我们有n部手机m层楼,第1部手机在第
    k层摔死了,那么接下来要测试的就是n-1部手机k-1层楼的情况,如果没摔死,就测试n部手机m-k层楼的情况(人会有主观意识觉得
    楼层越高越容易摔死,但是那不一定,手机也有可能到1000层都摔不死呢,所以测试情况的时候我们可以只看层数,k+1m和1m-k是一样的)。
    综上所述,我们可以推出动态转移方程:dp[i][j]表示i部手机j层楼的最少测试次数,dp[i][j]=max(dp[i-1][k-1],dp[i][j-k])+1,
    k∈[1,j-1](这里取max是因为i部手机j层楼有dp[i][j]次测试机会,我们必须确保在这个次数内所有情况的耐摔指数都要能被测出来,
    如果取了较小的那个,次数多于它的情况就没法确保被测试出来)。我们一开始在给dp数组初始化的时候,可以全部初始化为它的最坏情况,
    j层楼的最坏测试次数是j次,就是每层楼都要摔一次,那么我们这里用了最佳策略后,次数就应该小于等于这个值,方程就可以变化为:
    dp[i][j]=min(dp[i][j],max(dp[i-1][k-1],dp[i][j-k])+1),k∈[1,j-1]。
    【题解】

    #include <iostream>
    using std::cout;
    using std::endl;
    int dp[5][1005];
    void solve(int phone, int floor)
    {
        for(int i = 1; i <= phone; i++)
            for(int j = 1; j <= floor; j++)
                dp[i][j] = j;               // 
        for(int i = 2; i <= phone; i++)     // 
            for(int j = 1; j <= floor; j++)
                for(int k = 1; k < j; k++)  // 
                    dp[i][j] = std::min(dp[i][j], std::max(dp[i-1][k-1], dp[i][j-k]) + 1);
        
    }
    int main()
    {
        solve(3, 1000);
        cout << dp[3][1000] << endl;
        return 0;
    }

    5、快速排序

    【题目】
    快速排序代码补全,这里直接给出完整代码,该题快排采用递归实现。
    【题解】

    #include <iostream>
    #include <vector>
    #include <list>
    #include <numeric>
    #include <string>
    #include <stdio.h>
    #include <queue>
    #include <string.h>
    #include <cmath>
    #include <algorithm>
    #include <set>
    using std::endl;
    using std::cout;
    
    void quickSort(int *arr, int begin, int end)
    {
        if(begin < end)
        {
            int temp = arr[begin];
            int i = begin;
            int j = end;
            while(i < j)
            {
                while(i < j && arr[j] > temp)
                    j--;
                arr[i] = arr[j];
                while(i < j && arr[i] <= temp)
                    i++;
                arr[j] = arr[i];
            }
            arr[i] = temp;
            // 递归排序基准数两边子集
            quickSort(arr, begin, i-1);
            quickSort(arr, i+1, end);
        }
        else return;
    }
    int main()
    {
        int num[10] = {23, 14, 5, 7, 29, 50, 11, 33, 10, 8};
        cout << "排序前: " << endl;
        for(int i = 0; i < 10; i++)
            cout << num[i] << " ";
        cout << endl;
        quickSort(num, 0, 9);
        cout << "排序后: " << endl;
        for(int i = 0; i < 10; i++)
            cout << num[i] << " ";
        cout << endl;
        return 0;
    }

    6、递增三元组

    【题目】
    给定三个整型数组
    A = [A1, A2, … An]
    B = [ … ]
    C = [ … ]
    统计有多少个三元组(i, j, k)满足:
    1、 1 <= (i, j, k) <= n
    2、 Ai < Bj < Ck

    输入示例:
    3
    1 1 1
    2 2 2
    3 3 3

    输出示例:
    27
    【题解】

    #include <iostream>
    #include <vector>
    #include <list>
    #include <numeric>
    #include <string>
    #include <stdio.h>
    #include <queue>
    #include <string.h>
    #include <cmath>
    #include <algorithm>
    #include <set>
    using std::endl;
    using std::cout;
    
    int main()
    {
        using std::cin;
        using std::sort;
        int n, flag = 0;
        long long ans = 0;
        const int N = 10005;
        cin >> n;
        int *A = new int[n];
        int *B = new int[n];
        int *C = new int[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];
        // // 暴力
        // for(int i = 0; i < n; i++)
        //     for(int j = 0; j < n; j++)
        //         for(int k = 0; k < n; k++)
        //         {
        //             if(A[i] < B[j] && B[j] < C[k])
        //                 ans++;
        //         }
        // 优化
        int temp = 0;
        sort(A, A + n);
        sort(B, B + n);
        sort(C, C + n);
        for(int i = 0; i < n; i++)
        {
            for(int j = temp; j < n; j++)
            {
                if(B[j] > A[i])
                { 
                    if(!flag) { temp = j; flag = 1; } 
                    ans += C + n - std::upper_bound(C, C+n, B[j]);
                }
            }
            flag = 0;
        }
        cout << ans << endl;
        delete[] A, B, C;
        return 0;
    }

    7、螺旋折线

    【题目】
    如下图所示的螺旋折线经过平面上所有整点恰好一次
    对于整点(X, Y),我们定义它到原点的距离dis(X, Y)是从原点到(X, Y)的螺旋折线段的长度。
    例如dis(0, 1)=3, dis(-2, -1)=9
    给出整点坐标(X, Y),你能计算出dis(X, Y)吗?
    在这里插入图片描述

    输入样例:
    0 1
    输出样例:
    3

    测试数据:
    输入——>
    1 0
    2 0
    3 0
    -1 0
    2 2
    3 2
    -1 2
    输出——>
    5 18 39 1 16 37 13

    【思路】
    分类讨论+找规律,先找四个象限的角点的规律,如图可以看到第一二四象限的角点各自与x轴y轴都形成了一个小正方形,
    设这个小边长为t。第一象限的角点规律是4t2,当x>y时,t就是x,那么在这个范围内的点就是4t2+(x-y);
    当x<y时,t就是y,那么在这个范围内的点就是4t^2-(y-x)。第二象限的角点规律是2t(2t-1),当abs(x)>y时,
    t就是-x,那么在这个范围内的点就是2t(2t-1)-(-x-y);当abs(x)<y时,t就是y,那么在这个范围内的点就是
    2t(2t-1)+(y-(-x))。第四象限的角点规律是2t(2t+1),当x>abs(y)时,t就是x,那么在这个范围内的点就是
    2t(2t+1)-(x-(-y));当x<abs(y)时,t就是-y,那么在这个范围内的点就是2t(2t+1)+(-y-x)。第三象限就
    有点复杂了,它的角点规律是(-x-y)^2,由于在第三象限形成的是个小矩形,所以我们并不好找矩形,但是至少可
    以看出这个角点上x和y的差值为1,这里不用t表示边长,这里用t表示角点的(-x-y)是多少,当abs(x)>abs(y)时,
    t=-x-x-1,那么这个范围内的点就是t2+(-x-1-(-y)),否则,t=-y-y+1,范围内的点就是t2-(-y+1-(-x))。
    在这里插入图片描述

    【题解】

    #include <iostream>
    #include <vector>
    #include <list>
    #include <numeric>
    #include <string>
    #include <stdio.h>
    #include <queue>
    #include <string.h>
    #include <cmath>
    #include <algorithm>
    #include <set>
    using std::endl;
    using std::cout;
    
    int main()
    {
        using std::max;
        long long x, y, t, ans = 0;
        std::cin >> x >> y;
        // 四种情况
        if(x >= 0 && y >= 0)        { t = max(x, y); ans = 4 * t * t + x - y; }
        else if(x < 0 && y >= 0)    { t = max(-x, y); ans = 2 * t * (2 * t - 1) + x + y; }
        else if(x >=0 && y < 0)     { t = max(x, -y); ans = 2 * t * (2 * t + 1) - x - y; }
        else
        {
            if(x < y)   t = -x - x - 1;
            else        t = -y - y + 1;
            ans = t * t + - x - 1 + y;
        }
        cout << ans << endl;
        return 0;
    }

    8、日志统计

    【题目】
    小明维护着一个程序员论坛。现在他收集了一份"点赞"日志,日志共有N行。其中每一行的格式是:
    ts id
    表示在ts时刻编号id的帖子收到一个"赞"。
    现在小明想统计有哪些帖子曾经是"热帖"。如果一个帖子曾在任意一个长度为D的时间段内收到不少于K个赞,小明就认为这个帖子曾是"热帖"。
    具体来说,如果存在某个时刻T满足该帖在[T, T+D)这段时间内(注意是左闭右开区间)收到不少于K个赞,该帖就曾是"热帖"。
    给定日志,请你帮助小明统计出所有曾是"热帖"的帖子编号。

    输入格式:
    第一行包含三个整数N、D和K。
    以下N行每行一条日志,包含两个整数ts和id。

    输出格式:
    按从小到大的顺序输出热帖id。每个id一行。

    输入样例:
    7 10 2
    0 1
    0 10
    10 10
    10 1
    9 1
    100 3
    100 3

    输出样例:
    1
    3
    【思路】
    把每个日志获得的点赞信息存储好,按时间排序,用尺取法r在前l在后,当点赞数大于等于k,
    判断时间间隔,不满足就l往前取,r继续往后取,直到点赞数大于等于k执行相同判断.
    用set容器存储id方便访问并且可以达到输入去重的目的。
    【题解】

    #include <iostream>
    #include <vector>
    #include <list>
    #include <numeric>
    #include <string>
    #include <stdio.h>
    #include <queue>
    #include <string.h>
    #include <cmath>
    #include <algorithm>
    #include <set>
    using std::endl;
    using std::cout;
    using std::vector;
    using std::set;
    
    #define N 100005
    vector<int> t[N];
    set<int> s;
    int n, d, k;
    
    bool judge(int x)
    {
        int len = t[x].size();
        if(len < k) return false;
        std::sort(t[x].begin(), t[x].end());
        int l = 0, r = 0, sum = 0;
        while(l <= r && r < len)
        {
            sum++;
            if(sum >= k)
            {
                if(t[x][r] - t[x][l] < d)   return true;
                else    { l++; sum--; }
            }
            r++;
        }
        return false;
    }
    int main()
    {
        std::cin >> n >> d >> k;
        for(int i = 0; i < n; i++)
        {
            int id, ts;
            std::cin >> ts >> id;
            t[id].push_back(ts);
            s.insert(id);
        }
        for(auto it = s.begin(); it != s.end(); it++)
        {
            if(judge(int(*it))) 
                cout << *it << endl;
        }
        return 0;
    }

    9、淹没岛屿(BFS)

    【题目】
    你有一张某海域NxN像素的照片,".“表示海洋、”#"表示陆地,如下所示:
    … …
    .##…
    .##…
    …##.
    …####.
    …###.
    … …

    其中"上下左右"四个方向上连在一起的一片陆地组成一座岛屿。例如上图就有2座岛屿。 ?
    由于全球变暖导致了海面上升,科学家预测未来几十年,岛屿边缘一个像素的范围会被海水淹没。
    具体来说如果一块陆地像素与海洋相邻(上下左右四个相邻像素中有海洋),它就会被淹没。 ?
    例如上图中的海域未来会变成如下样子:
    … …
    … …
    … …
    … …
    …#…
    … …
    … …

    请你计算:依照科学家的预测,照片中有多少岛屿会被完全淹没。

    输入格式:
    7
    … …
    .##…
    .##…
    …##.
    …####.
    …###.
    … …

    输出格式:
    1
    【解题思路】
    BFS,找出一共有多少个连通块(岛屿定义方法即为连通块定义),判断连通块如果每个"#“都连着哪怕一个”.",那么这样的岛屿会完全消失,只要有一个"#“的上下左右都不是”."的时候,就不会完全消失。
    【题解】

    #include <iostream>
    #include <vector>
    #include <list>
    #include <numeric>
    #include <string>
    #include <stdio.h>
    #include <queue>
    #include <string.h>
    #include <cmath>
    #include <algorithm>
    #include <set>
    using std::endl;
    using std::cout;
    using std::queue;
    using std::cin;
    #define N 1000
    
    char a[N][N];
    int n, vis[N][N];
    int dir[4][2] = {{-1, 0}, 
                    {0, -1}, 
                    {0, 1}, 
                    {1, 0}};
    class point
    {
    public:
        int x, y;
        point(int a, int b) { x = a; y = b; }
    };
    
    int bfs(int x, int y)   // 用来判断每个连通块是否存在符合条件的"#"
    {
        queue<point> q;
        q.push(point(x, y));
        int left = 0;
        while(!q.empty())
        {
            point p = q.front();
            q.pop();
            int cnt = 0;
            for(int i = 0; i < 4; i++)  // 四方向判断
            {
                int dx = p.x + dir[i][0], dy = p.y + dir[i][1];
                if(dx < 0 || dx >= n || dy < 0 || dy >= n)  // 边界判断
                    continue;
                if(a[dx][dy] == '#')
                {
                    cnt++;
                    if(!vis[dx][dy])
                    {
                        vis[dx][dy] = 1;
                        q.push(point(dx, dy));
                    }
                }
            }
            if(cnt == 4)
                left++;
        }
        return left;    // 返回一个岛(连通块)淹没后残留的"#"数量
    }
    int main()
    {
        int i, j, ans = 0;
        cin >> n;
        for(i = 0; i < n; i++)
            for(j = 0; j < n; j++)
                cin >> a[i][j];
        memset(vis, 0, sizeof(vis));
        for(i = 0; i < n; i++)
            for(j = 0; j < n; j++)
                if(a[i][j] == '#' && !vis[i][j])
                {
                    vis[i][j] = 1;
                    if(!bfs(i, j))
                        ans++;
                }
        cout << ans << endl;
        return 0;
    }

    10、最大乘积

    【题目】
    给定N个整数A1, A2, … AN。请你从中选出K个数,使其乘积最大。
    请你求出最大的乘积,由于乘积可能超出整型范围,你只需输出乘积除以1000000009的余数。
    注意,如果X<0, 我们定义X除以1000000009的余数是负(-X)除以1000000009的余数。
    即:0-((0-x) % 1000000009)

    输入格式:
    第一行包含两个整数N和K。
    以下N行每行一个整数Ai。
    对于40%的数据,1 <= K <= N <= 100
    对于60%的数据,1 <= K <= 1000
    对于100%的数据,1 <= K <= N <= 100000 -100000 <= Ai <= 100000

    输出格式:
    一个整数,表示答案。

    输入样例:
    5 3
    -100000
    -10000
    2
    100000
    10000

    输出样例:
    999100009
    再例如:

    输入样例:
    5 3
    -100000
    -100000
    -2
    -100000
    -100000

    输出样例:
    -999999829

    【解题思路】
    把这些数按照绝对值从大到小排序:
    ①当n==k时,只能全选了
    ②如果n个数全是正数,选择前k个即可
    ③如果n个数全是负数,如果k是偶数,选择前k个即可;如果k是奇数,选择后k个即可
    ④剩下的情况就是有正有负了,先看前k个数,如果负数个数为偶数,选择前k个即可;如果负数个数为奇数,则比较前面最后一个负数与后面第一个负数的乘积和前面最后一个正数与后面第一个正数的乘积哪个大,取大的即可
    参考:https://blog.csdn.net/ryo_218/article/details/79822728

    【题解】

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<iostream>
    #include<algorithm>
    #include<string>
    #include<vector>
    #include<queue>
    #include<map>
    #include<set>
    using namespace std;
     
    #define N 100000
    #define MOD 1000000009
     
    struct number
    {
        int sign;
        long long num;
    }a[N];
     
    bool cmp(const number &x,const number &y)
    {
        return x.num>y.num;
    }
     
    int main()
    {
        int n,k,cnt=0;
        long long t,ans=1;
        cin>>n>>k;
        for(int i=0;i<n;i++)
        {
            cin>>t;
            if(t<0)
            {
                a[i].sign=-1;
                a[i].num=-t;
                cnt++;
            }
            else
            {
                a[i].sign=1;
                a[i].num=t;
            }
        }
        if(n==k)
        {
            for(int i=0;i<k;i++)
                ans=(ans*a[i].num)%MOD;
            if(cnt&1)
                ans=-ans;
        }
        else
        {
            sort(a,a+n,cmp);
            if(cnt==n)//全是负数 
            {
                if(k&1)
                {
                    for(int i=n-1;i>n-1-k;i--)
                        ans=(ans*a[i].num)%MOD;
                    ans=-ans;
                }
                else
                {
                    for(int i=0;i<k;i++)
                        ans=(ans*a[i].num)%MOD;
                }
            }
            else if(cnt==0)//全是正数 
            {
                for(int i=0;i<k;i++)
                    ans=(ans*a[i].num)%MOD;            
            }
            else
            {
                int nega=-1,posi=-1;
                cnt=0;
                for(int i=0;i<k;i++)
                {
                    if(a[i].sign==-1) 
                    {
                        //nega记录前K个中最后一个负数的下标
                        //cnt记录前k个中负数的个数,每满2个就乘一次 
                        cnt++;
                        if(nega==-1)
                            nega=i;
                        else
                        {                        
                            if((cnt&1)==0)//保持负数是一对的样子乘上去 
                            {
                                ans=(ans*a[nega].num)%MOD;
                                ans=(ans*a[i].num)%MOD;
                            }
                            nega=i;
                        }
                    }
                    else
                    {
                        //posi记录前k个中最后一个正数的下标
                        //每次只乘上前一个正数,留最后一个正数作交换 
                        if(posi==-1)
                            posi=i;
                        else
                        {
                            ans=(ans*a[posi].num)%MOD;
                            posi=i;
                        }
                    }
                }
                if((cnt&1)==0)//负数为偶数个,则直接取前k个数即可,把最后一个正数乘上即可 
                    ans=(ans*a[posi].num)%MOD;
                else
                {
                    int nega1=-1,posi1=-1;
                    for(int i=k;i<n;i++)
                    {
                        if(a[i].sign==-1)//找绝对值最大的负数 
                        {
                            if(nega1==-1)
                                nega1=i;
                        }
                        else//找最大的正数 
                        {
                            if(posi1==-1)
                                posi1=i;
                        }
                        if(nega1!=-1&&posi1!=-1)
                            break;
                    }
                    if(nega1!=-1&&posi1!=-1)
                    {
                        //比较是前k个中最后一个负数和后面绝对值最大的负数乘积大
                        //还是前k个中最后一个正数和后面最大的正数乘积大 
                        if(a[nega].num*a[nega1].num>a[posi].num*a[posi1].num)
                        {
                            ans=(ans*a[nega].num)%MOD;
                            ans=(ans*a[nega1].num)%MOD;
                        }
                        else
                        {
                            ans=(ans*a[posi].num)%MOD;
                            ans=(ans*a[posi1].num)%MOD;                        
                        }
                    }
                    else if(posi1!=-1)//后面只有正数了 
                    {
                        ans=(ans*a[posi].num)%MOD;
                        ans=(ans*a[posi1].num)%MOD;    
                    }
                    else//后面只有负数了 
                    {
                        ans=(ans*a[nega].num)%MOD;
                        ans=(ans*a[nega1].num)%MOD;
                    } 
                }
            }
        }
        cout<<ans<<endl;    
        return 0;
    }

    说明:由于是练手代码,所有题解源码未专门做优化处理,仅供参考,谢谢阅读!!!
    感谢:3 ~ 9届蓝桥杯比赛原题点击传送

  • 相关阅读:
    如何选择机器学习算法 转
    机器学习经典算法详解及Python实现--基于SMO的SVM分类器
    机器学习(Machine Learning)&深度学习(Deep Learning)资料
    计算智能在设备状态维护中的应用
    LaTeX 在编译时出现 File ended while scanning use of @writefile错误
    LaTeX 中插入图片使其紧跟插入的文字之后
    LaTeX 制作表格
    LaTeX 中换段落
    LaTeX 中使用三级标题
    使用 WinEdt 来写中文文章or 建模论文
  • 原文地址:https://www.cnblogs.com/horacle/p/13167753.html
Copyright © 2011-2022 走看看