藍橋杯第九屆(2018)B組省賽3-10題練手源碼

 

三、求乘積末尾零個數

【題目】
以下的10行數據,每行有10個整數,請你求出它們的乘積的末尾有多少個零?ios

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;
}

四、手機測試

【題目】
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;
}

 

五、快速排序

【題目】
快速排序代碼補全,這裏直接給出完整代碼,該題快排採用遞歸實現。
【題解】ui

#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;
}

 

六、遞增三元組

【題目】
給定三個整型數組
A = [A1, A2, … An]
B = [ … ]
C = [ … ]
統計有多少個三元組(i, j, k)知足:
一、 1 <= (i, j, k) <= n
二、 Ai < Bj < Ckspa

輸入示例:
3
1 1 1
2 2 2
3 3 3.net

輸出示例:
27
【題解】3d

#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;
}

 

七、螺旋折線

【題目】
以下圖所示的螺旋折線通過平面上全部整點剛好一次
對於整點(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;
}

 

八、日誌統計

【題目】
小明維護着一個程序員論壇。如今他收集了一份"點贊"日誌,日誌共有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;
}

 

九、淹沒島嶼(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;
}

 

十、最大乘積

【題目】
給定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屆藍橋杯比賽原題點擊傳送

相關文章
相關標籤/搜索