【算法比赛】竞码编程-蓝桥杯模拟赛3

比赛:

http://oj.hzjingma.com/contest/view?id=69


 

试题A:生存还是毁灭,这是一个问题(字符串遍历)

试题B:小小神枪手 开局98K(数学:概率论)

试题C:关云长单刀会金莲,贾宝玉三打白骨精(组合数学或者暴力枚举)

试题D:抽刀断水水更流,举杯销愁愁更愁(二进制枚举)

试题E:左手作圆右手方,世人机敏便可尔(数学计算)

试题F:等差等比有联系 公差公比求通项(思路 + 模拟 + 快速幂)

试题G:进退得失全看透,名利当作粪土丢(贪心)

试题H:映日圆光万颗余,如观宝藏隔虾须(BFS,不懂思路,知道要用BFS)

试题I:自知无奈的回绝,奇迹是否能够出现(不会)

试题J:世界末日的时候,我会牵着你的手(不会)

 


试题A:生存还是毁灭,这是一个问题(字符串遍历)

 

利用编程,读入,依此遍历,统计字母个数。最后输出字母个数最大值

#include
using namespace std;

int cnt[30], A[30];

int main()
{
    memset(cnt, 0, sizeof(cnt));
    memset(A, 0, sizeof(A));

    string s;
    vector passages;
    freopen("input.txt", "r", stdin);
    while(cin >> s)
    {
        passages.push_back(s);
    }

    for(auto p : passages)
    {

        for(int i = 0;i < p.size(); ++i)
        {
            // 不区分大小写
            if(p[i] >= 'a' && p[i] <= 'z') ++cnt[p[i] - 'a'];
            if(p[i] >= 'A' && p[i] <= 'Z') ++cnt[p[i] - 'A'];
        }
        cout << endl;
    }
    
    int res = 0;
    for(int i = 0;i < 26; ++i)
    {
        res = max(res, cnt[i]);
    }
    cout << res << endl;


    return 0;
}

 


试题B:小小神枪手 开局98K(数学:概率论)

 

根据期望的计算公式:E = \sum P_i * V_i。其中在这里,V 是射击次数,P 是对应的概率

当 V = 1 时,也就是说,第一枪中了,那么概率是  0.75

当 V = 2,第一枪不中,第二枪中,那么概率是 (1 - 0.75) * (0.75 * 0.9)

当 V = 3,第一、二枪都不中,第三枪中,那么概率 (1 - 0.75) * (1 - 0.75 * 0.9) * (0.75 * 0.9 * 0.9)

直到射中概率 < 0.5 为止。

那么在程序中,也就是一个变量记录 第几次射击,一个变量记录前几次都不中(也就是不断的累成,上一次不中的概率),一个变量记录 这一次 中的概率。(期望就是,每一个的 涉及次数 * 这次的概率的累加)

 

#include
using namespace std;

int main()
{
    int cnt = 1;  // 第几次射击
    double res = 0;
    double get = 0.75;  // 这次中的概率
    double p = 1;  // 前几次都不中概率
    while(get >= 0.5)
    {
        res += p * get * cnt;  // 第几次射击时的期望累加

        ++cnt;     // 射击次数增加
        p *= (1.0 - get);   // 相当于上一次不中,也就是累乘不中概率
        get *= 0.9;   // 这次中的概率

    }

    printf("%0.6lf\n", res);
    return 0;
}

 


试题C:关云长单刀会金莲,贾宝玉三打白骨精(组合数学或者暴力枚举)

 

思路一、组合数学

也就是将 四大名著需要的时间看成一个整体,也就是 5,4,3,3,各自看成一个整体,那么剩下还有 16 天(这 16 天有 17个空),那么插入的时候,总共的可能为:17 * 18 * 19 * 20 = 116280。(一开始是 17 个空,插入一个 ;18个空,再插入一个;19个空,插入一个;20个空,最后插入一个)

 

思路二、暴力枚举

我们通过枚举每一个名著的开始阅读时间,然后判断这种可能方案,满不满足要求。也就是每一本名著读书的那天,不能读其他的。

我们可以用一个变量 vis,记录每一天是否已经读过了,如果已经读过,我们还读,那么这个方案就不可能。

 

#include
using namespace std;

bool isCan(int a, int b, int c, int d)
{
    int vis[40];
    memset(vis, 0, sizeof(vis));  // 记录 31 天的读书情况

    for(int i = 0; i < 5; ++i) vis[a + i] = 1;  // 第一本名著 5 天
    for(int i = 0;i < 4; ++i)  // 第二本 4 天
    {
        if(vis[b + i] == 1) return false;   // 如果这天已经读了,那么就不能再读了
        vis[b + i] = 1;
    }
    for(int i = 0;i < 3; ++i)
    {
        if(vis[c + i] == 1) return false;
        vis[c + i] = 1;
    }
    for(int i = 0;i < 3; ++i)
    {
        if(vis[d + i] == 1) return false;
        vis[d + i] = 1;
    }
    return true;
}

int main()
{
    int cnt = 0;
    
    for(int a = 1; a <= 27; ++a)  // 5
    {
        for(int b = 1; b <= 28; ++b)  // 4
        {
            for(int c = 1; c <= 29; ++c)  // 3
            {
                for(int d = 1;d <= 29; ++d)   // 3
                {
                    if(isCan(a, b, c, d)) ++cnt;
                }
            }
        }
    }

    cout << cnt << endl;
    return 0;
}

 


试题D:抽刀断水水更流,举杯销愁愁更愁(二进制枚举)

 

总共 22 个数,选择其中的 0 -12 个数,加上来组成一个新数。

我们可以用二进制枚举,对于 22 个数,每一个数,只有拿或不拿两种情况,也就是 0 或者 1。所以总共有 2 ^ 22 约等于 4e6。不会超时。

因为我们用二进制枚举,每一位对应这个数要不要取,如果取,那就累和。还要注意,最后只能取 12 个,所以我们要判断,这种取法中 1 的个数,如果是 >12 ,那这种方案不成立。

然后算出所有情况的数,用 set 统计(可能有重复的,去重)。

最后答案是问,无法构成的个数,因此答案是 : 总数(1695) - set 中的数(可以构成了这么多数)

 

#include
using namespace std;

int nums[22] = {3,5,7,11,13,19,23,29,31,37,41,53,59,61,67,71,97,101,127,197,211,431};

set recd;

int main()
{
    recd.clear();
    int lim = (1 << 22);

    for(int i = 0;i < lim; ++i)
    {
        int cnt = 0;
        int res = 0;
        int tep = i;
        for(int j = 0;j < 22; ++j)
        {
            if((tep >> j) & 1)
            {
                ++cnt;
                res += nums[j];
            } 
        }
        if(cnt <= 12) 
        {
            recd.insert(res);
        }
    }
    int cnt = 1695;
    cout << cnt - recd.size() << endl;

    return 0;
}

试题E:左手作圆右手方,世人机敏便可尔(数学计算)

根据题目要求,也就是计算值,我们通过画图可以发现

【算法比赛】竞码编程-蓝桥杯模拟赛3_第1张图片

也就是一个三角形的面积的四倍,一个扇形面积的四倍。

【算法比赛】竞码编程-蓝桥杯模拟赛3_第2张图片

三角形的面积好求,两条边,勾股定理求底,然后 S = 1 / 2 * 底 * 高

对于扇形面积 : \frac{1}{2}\Theta R^2,其中θ是弧度制的角度,我们可以通过求三角形的那个角度的弧度(cosA = 4 / 3),得到 A 之后,我们发现 A  + A + θ = PI / 2。那么就可以求出 θ了。

剩下就是代码求数值了

#include
using namespace std;

const double PI = 3.14159265358979;

int main()
{
    double res = 3.0 * sqrt(7.0) + 8.0 * (PI / 2.0 - 2.0 * acos(3.0 / 4.0));

    printf("%0.2lf\n", res);
    return 0;
}

 


试题F:等差等比有联系 公差公比求通项(思路 + 模拟 + 快速幂)

我们通过 v1 = b / a, v2 = c / b。因为要最大,所以要求出来的公比 q 尽可能的大

其中, v1 和 v2 应该是,找到一个最大的 q 的幂都满足可以 = v1,和 = v2。

几个特殊情况 :

1)当 b = a,或者 c = b 的时候,那么公比就是 q = 1

2)如果 v1 == v2,那么直接 q = v1 即可。

剩下的情况就是,我们已经知道 v1(将 v1 和 v2 中的最小值保存在 v1 中),不会是 公比,那么最大的可能应该是从 开方开始找(因为不会是 q = v1,那么为了使得 q 尽可能大,那么 q ^ 2 = v1,q = sqrt(v1),一直到  2 ,枚举 q)

当判断出 这个  q 和 v1,v2 都是幂的关系(也就是比如,q ^ x = v1,也就是说,v1 % q == 0,一直到  v1 == 1为止。不然就是不满足  q ^ x = v1 )

最后得到 公比 q 之后,那么第 N 项,根据公式,就是 an = a1 * q ^ (n - 1)。

由于 n 的数据范围,所以我们要使用快速幂进行解决。

 

#include
using namespace std;

#define LL long long

const LL MOD = 1e9;

LL qpow(LL x, LL y)
{
    LL ans = 1;
    while(y)
    {
        if(y & 1) ans = ans * x % MOD;
        x = x * x % MOD;
        y >>= 1;
    }
    return ans;
}

bool isCan(LL x, LL y)  // x == y ^ n
{
    while(x)
    {
        if(x == 1) return true;
        if(x % y != 0) return false;

        x /= y;
    }
}

int main()
{
    LL a, b, c, N;
    cin >> a >> b >> c >> N;

    if(a == b || a == c) cout << a << endl;
    else
    {
        LL res = 0, q = 0;
        LL v1 = b / a, v2 = c / b;

        if(v1 > v2) swap(v1, v2);

        if(v1 == v2) q = v1;
        else
        {
            q = v1;
            int temp = sqrt(v1);

            for(int i = temp + 1; i >= 2; --i)
            {
                if(isCan(v1, i) && isCan(v2, i))
                {
                    q = i;
                    break;
                }
            }
        }

        res = a * qpow(q, N - 1) % MOD;
        cout << res << endl;
    }

    return 0;
}

试题G:进退得失全看透,名利当作粪土丢(贪心)

 

我们对于某一个天的需求,这个时候可能还有前几天的剩货,那么先用剩货补货。

如果补货了,还差,那就要新买

而对于买,由于可以提起买,我们就找,这一天以及前面的所有最便宜的(这样子使得花费最小,而且是可以的,因为这样子,我们相当于是提前在前面买的)。

所以,对于购买价格,我们一直是找,这一天以及前面的所有价格中的最小值来购买即可。

 

要特别注意的,由于买一盒柠檬是得到 5 * 16 = 80 片,所有要注意。同时,对于要新买的柠檬,如果不能被 80 整除,说明我们要多买一点儿,同时这样子,会有剩货(要记录)

 

#include 
using namespace std;

#define LL long long

int main()
{
    LL n, a, b;
    cin >> n >> a >> b;
    
    LL miPa = 1000, miPb = 1000;
    LL hasPa = 0, hasPb = 0;

    LL res = 0;

    for(int i = 0;i < n; ++i)
    {
        LL c, pa, pb;
        cin >> c >> pa >> pb;

        miPa = min(miPa, pa);
        miPb = min(miPb, pb);

        res += c * a * miPa;

        if(hasPb >= c * b)  // 剩货超过需要的,那么不需要重新买
        {
            hasPb -= c * b;
        }
        else  // 否则,说明不够,要重新买
        {
            LL needPb = c * b - hasPb;
            hasPb = 0;

            if(needPb % 80 == 0)
            {
                res += (needPb / 80) * miPb;
                
            }
            else
            {
                LL tep = (needPb / 80) + 1;   // 不被整除,说明多买一盒
                res += tep * miPb;
                hasPb = tep * 80 - needPb;   // 多出来的剩货,要记录
            }
        }

    }
    cout << res << endl;

    return 0;
}

试题H:映日圆光万颗余,如观宝藏隔虾须(BFS,不懂思路,知道要用BFS)

 

这道题,知道是用BFS,但是不知道思路是怎么样的。

代码可以看链接,其他人提交的,但是不知道思路,所以看不太懂。

 


试题I:自知无奈的回绝,奇迹是否能够出现(不会)

 

代码可以看链接,其他人提交的,但是不知道思路,所以看不太懂。


试题J:世界末日的时候,我会牵着你的手(不会)

 

代码可以看链接,其他人提交的,但是不知道思路,所以看不太懂。

你可能感兴趣的:(#,竞码编程的比赛)