CCW2021蓝桥杯选拔赛(一)

C - 云顶之弈!

最近小明很闲,找了小华下棋。 棋盘的大小是n*m,小明突然想到一个新的玩法,首先有个卒放在棋盘的右上角(1,m)的位置。 每一次小明或者小华可以将这个卒向左移一步或者向下移一步,或者向左下移一步 谁不能移动谁就输了。小明先移动棋子卒,小明会赢吗?假设玩家都是最优决策。
Input
多组输入,每行包括两个数字n,m (0 < n , m < = 2000 )当n=0,m=0时输入结束.
Output
如果小明获胜则重拳出击print"Wonderful!",否则bb一句print"What a pity!"
Sample Input
5 3
5 4
6 6
0 0
Sample Output
What a pity!
Wonderful!
Wonderful!

题解
一个以前做过的博弈题目,如果棋子处于左边界,那么只能向下移动,同理如果棋子处于下边界,就只能向左移动,这两种情况下,输赢很好判断。然后再缩减一圈判断,直到判断到全图就行了。解法很奇妙,详细步骤不写了,太多了。
最后的规律是,只要n和m有一个是奇数,先手赢,否则后手赢。
AC代码:

#include <bits/stdc++.h>
using namespace std;
int main(void)
{
    int n, m;
    while (cin >> n >> m)
    {
        if (n == 0 && m == 0)
            break;
        if (n % 2 == 1 && m % 2 == 1)
            cout << "What a pity!" << endl;
        else
            cout << "Wonderful!" << endl;
    }
}

D - 签到啦!

蓝桥选拔赛终于开始了,话不多说A题吧。
从前从前有一个正整数n,你需要找到一个素数x和一个合数y使x+y=n成立,这样就可以双剑合并了。
素数是一个大于1的自然数,它的因数只有1与它自己本身。非素数且大于1的自然数称为合数。当然,1不是素数也不是合数。

Input
输入包含多个样例。输入的第一行包含一个整数T (1≤T≤10^5),样例数目。

对于每个样例,输入的每一行都包含一个整数n (1≤n≤10^9).

Output
对于每一种情况,打印两个整数。x和y中1≤x,y<n。
如果有多个有效答案,随便您打印任何一个。如果没有有效答案,则打印整数−1。

Example
Input
3
4
6
7
Output
-1
2 4
3 4

题解
签到题刚开始我还不会写。。。错了几次才发现规律。

如果n<=5,无解,否则
如果n是偶数,那么 n-2 一定是合数,所以可以拆成 2 和 n-2。
如果n是奇数,那么 n-3 一定是合数,所以可以拆成 3 和 n-3。
AC代码:

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int t;
    cin >> t;
    while (t--)
    {
        int n;
        cin >> n;
        if (n <= 5)
        {
            cout << "-1" << endl;
            continue;
        }
        if (n % 2 == 0)
            cout << 2 << ' ' << n - 2 << endl;
        else
            cout << 3 << ' ' << n - 3 << endl;
    }
    return 0;
}

E - 栈!

给你一串字符,不超过50个字符,可能包括括号、数字、字母、标点符号、空格,你的任务是检查这一串字符中的( ) ,[ ],{ }是否匹配。

Input
输入数据有多组,每组数据不超过100个字符并含有( ,) ,[, ],{, }一个或多个。处理到文件结束。

Output
如果匹配就输出“yes”,不匹配输出“no”

Sample Input
sin(20+10)

{[}]

Sample Output
yes

no

题解
括号匹配问题,会栈就行。
AC代码:

#include <iostream>
#include <cstring>
#include <stack>
using namespace std;
bool isright(int ch)
{
    return ch == ']' || ch == '}' || ch == ')';
}
bool isleft(int ch)
{
    return ch == '[' || ch == '{' || ch == '(';
}
bool ispair(int ch1, int ch2)
{
    return ch1 == '[' && ch2 == ']' || ch2 == '[' && ch1 == ']' ||
           ch1 == '{' && ch2 == '}' || ch2 == '{' && ch1 == '}' ||
           ch1 == '(' && ch2 == ')' || ch2 == '(' && ch1 == ')';
}
int main(void)
{
    char str[200];
    while (gets(str))
    {
        stack<int> s;
        int size = strlen(str);
        for (int i = 0; i < size; i++)
        {
            if (isright(str[i]))
            {
                if (s.size() == 0)
                {
                    cout << "no" << endl;
                    goto end;
                }
                else if (ispair(s.top(), str[i]))
                {
                    s.pop();
                }
                else
                {
                    cout << "no" << endl;
                    goto end;
                }
            }
            else if (isleft(str[i]))
            {
                s.push(str[i]);
            }
        }
        if (s.size() == 0)
            cout << "yes" << endl;
        else
            cout << "no" << endl;
    end:;
    }
}

F - 数列的高度

一个数列的高度,是指其所有递增的子列中最长的一个子列的长度

给定一个长度为 n 的数列 an,求这个数列的高度

例如对数列 1 7 2 8 3 4,这个数列的最长子列是 1 2 3 4,长度为 4;次长的长度为 3, 包括 1 7 8、1 2 3 等。

Input
第一行一个正整数 n,表示数列元素个数,n<=1000 第二行 n 个正整数,从左到右给出数列的每一项
Output
一行一个正整数,表示该数列的高度
Sample Input
8
5 1 6 8 2 4 5 10
Sample Output
5

题解

最长升序子序列问题。
AC代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1010;
int a[maxn], n;
int dp[maxn];
int fun(int i)
{
    int ans = 1;
    for (int j = i - 1; j >= 1; j--)
    {
        if (a[i] >= a[j])
            ans = max(ans, dp[i] + dp[j]);
    }
    return dp[i] = ans;
}
int solove()
{
    for (int i = 1; i <= n; i++)
        dp[i] = 1;
    int ans = 0;
    for (int i = 2; i <= n; i++)
    {
        ans = max(ans, fun(i));
    }
    return ans;
}
int main(void)
{    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    cout << solove() << endl;
    return 0;
}

G - 人潮汹涌

宴会上人潮汹涌,人来人往

已知宴会上有 n 个人

每个人认识宴会上的一些人,如果 a 认识 b,我们就说 a 和 b 的疏远度是 1,特别地,a 和 a 的疏远度是 0

a 和 c 可以通过一个同时认识他们俩的 b 作介绍来互相认识,这时,我们说 a 和 c 的疏远度是 a 和 b 的疏远度加上 b 和 c 疏远度

选定两个人,问如何作介绍,使得 a 和 b 的疏远度最小?无法介绍来认识输出 !

Input
第一行两个正整数 n 和 m,表示宴会的人数,和有多少对人互相认识 接下来 m 行,每行两个数 p,q,表示 p 和 q 互相认识 接下来一行两个正整数 a,b,表示询问 a 和 b 的最小疏远度
Output
可以认识输出最小疏远度,否则输出 !
Sample Input
3 2
1 2
1 3
2 3
Sample Output
2

题解
最短路径问题,AC代码,就是问a到b的最短路径是多少。
AC代码:

#include <bits/stdc++.h>
using namespace std;
const int maxe = 100100, maxv = 1100;
const int INF = unsigned(-1) >> 1;
int n, m, s, t;
struct edge
{
    int x, y;
};
edge es[maxe];
struct Graph
{
    int dis;
};
Graph G[maxv][maxv];
struct dist
{
    int dis;
};
dist d[maxv];
void fun(int s)
{
    for (int i = 0; i <= n; i++)
        d[i].dis = INF;
    d[s].dis = 0;
    while (true)
    {
        bool updata = false;
        for (int i = 0; i < m; i++) 
        {
            edge e = es[i];
            if (d[e.x].dis != INF) 
            {
                if (d[e.x].dis + G[e.x][e.y].dis < d[e.y].dis) 
                {
                    d[e.y].dis = d[e.x].dis + G[e.x][e.y].dis; 
                    updata = true;
                }
            }
            if (d[e.y].dis != INF) 
            {
                if (d[e.y].dis + G[e.x][e.y].dis < d[e.x].dis) 
                {
                    d[e.x].dis = d[e.y].dis + G[e.x][e.y].dis; 
                    updata = true;
                }
            }
        }
        if (updata == false) 
            break;
    }
}
int main()
{
    while (scanf("%d%d", &n, &m) != EOF)
    {
        for (int i = 0; i <= n; i++)
            for (int j = 0; j <= n; j++)
                G[i][j].dis = INF;
        for (int i = 0; i < m; i++)
        {
            scanf("%d%d", &es[i].x, &es[i].y);
                G[es[i].x][es[i].y].dis = G[es[i].y][es[i].x].dis = 1;
        }
        scanf("%d%d", &s, &t);
        fun(s);
        if (d[t].dis == INF)
            puts("!");
        else
            printf("%d\n", d[t].dis);
    }
    return 0;
}

I - 乌鸦坐飞机!

给你两个长度为n的数组a,b,请你计算出下图中表达式的结果的最简分数形式。

Input
第一行输入T,表示样例个数。
每个样例先输入n,表示数组长度。(n <= 8)
接下来输入两行长度为n的数组a和b。 (1 <= ai,bi <= 10)
Output
每个样例输出"Case #x: p q", x表示样例序数,从1开始,p/q表示结果的最简分数形式。 (结果为整数是q等于1)
Sample Input
1
2
1 1
2 3
Sample Output
Case #1: 1 2

Hint
第一个样例: 2/(1+3/1) = 1/2

题解
这题测试的时候没写出来,而且很奇怪。
这题找出递推式就行了,式子不难找,递推式如下:
在这里插入图片描述
因为我们求的是分数,直接用递推式计算出来的是double实数,在转换成分数绝对会有精丢失。
我们可以这样:
在这里插入图片描述
这样我们就能从后一项的分子分母求前后一项的分子分母了,逆推就行。
然后我们还能推算出第n项的分子是bn,分母是an:

在这里插入图片描述
然后我们从第n项向前逆推就行了。
AC代码:

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
ull a[15], b[15];
ull n;
void solove(ull &x, ull &y)
{
    ull nx, ny;
    ull t;
    for (ull i = n - 1; i >= 1; i--)
    {
        nx = b[i] * y;     //新的分子 等于 上一次的分母*b[i]
        ny = a[i] * y + x; //新的分母 等于 上一次的分子+a[i]*上一次的分母
        x = nx, y = ny;
    }
}
int main(void)
{
    ull t;
    cin >> t;
    for (ull j = 1; j <= t; j++)
    {
        cin >> n;
        for (ull i = 1; i <= n; i++)
            cin >> a[i];
        for (ull i = 1; i <= n; i++)
            cin >> b[i];
        ull x = b[n], y = a[n];
        solove(x, y);
        int t = __gcd(x, y);
        cout << "Case #" << j << ": " << x / t << " " << y / t << endl;
    }
    return 0;
}

可是很奇怪,我solove函数按照上面那样写没问题,可是我如果改成这样:

void solove(ull &x, ull &y)
{
    ull nx, ny;
    ull t;
    for (ull i = n - 1; i >= 1; i--)
    {
        nx = b[i] * y;     //新的分子 等于 上一次的分母*b[i]
        ny = a[i] * y + x; //新的分母 等于 上一次的分子+a[i]*上一次的分母
        ull t = __gcd(nx, ny);
        x = nx / t, y = ny / t;
    }
}

因为我每循环一次都约过分嘛,所以最终得到的x和y是最简分数,然后我就把主函数约分操作删了,可是 WA掉了 ,不知道怎么回事。

J - ICPC!

你可能已经知道一个标准的 ICPC参赛队正好由三名成员组成。然而,完美的团队有更多的限制。学生可以有一些专长:擅长打代码或擅长数学。一个人可以没有专长,但不能同时拥有两者。 因此,如果这个团队包括至少一个擅长打代码的成员,至少一个擅长数学的成员,并且正好由三个成员组成,那么这个团队就被认为是完美的。 你是一所非常流弊的大学的教练,你知道你的学生中有 c 个是擅长打代码的,m 个擅长数学,x 个没有专长的。 你能分配到的完美团队最多是多少? 请注意,有些学生可能没有团队,每个学生只能加入一个团队。
你被要求回答 q 个独立的问题。
Input
第一行包含一个整数 q (1≤q≤1e4) —查询数。 接下来的q行各包含三个整数 c 、m 和 x (0≤c、m、x≤1e8) ——分别是大学里擅长打代码的、擅长数学的和啥都不会的菜鸡学生(就是我)的数量。
请注意,没有学生两个都擅长。
Output
输出 q 个整数
按照它们在输入中给出的顺序排列。答案是你可以把你的学生分成的完全完美的团队的最大数量。
Example
输入:
6
1 1 1
3 6 0
0 0 0
0 1 1
10 1 10
4 4 1
输出:
1
3
0
0
1
3

题解
这题也简单,不过最后看到这题,没时间写了。
假设三类人是a类b类和c类,c类是那个啥也不会的。
第一步:
在这里插入图片描述
如图所示,我们可以从A、B、C里面选出7组,因为A、B、C最小是7。
选出去之后我们更新一下数据:
在这里插入图片描述
第二步:
我们从求出中A、B最小的那个是8,C无论是多少的不用管。
然后我们把A比B多的5个人暂时当作C类:
在这里插入图片描述
这个时候我们就又能选出5组了。
第三步:
在这里插入图片描述
选出5组人后,到了第三步。这时,A、B的人数肯定是一样的。
然后 (A+B)/3 ,就是剩下的A、B两类人能选出的组数了,这里是2组。

所以例子的最终答案是:7+5+2=14.
AC代码:

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
int main(void)
{
    long long q;
    cin >> q;
    while (q--)
    {
        ll a, b, c;
        cin >> a >> b >> c;
        ll ans = min(min(a, b), c);
        a -= ans, b -= ans, c -= ans;
        if (a < b)
            swap(a, b);
        c += a - b;
        a = b;
        ans += min(b, c);
        a -= min(b, c), b -= min(b, c), c -= min(b, c);
        ans += (a + b) / 3;
        cout << ans << endl;
    }

    return 0;
}

K - 房子塌了!

由于达拉崩吧斑得贝迪卜多比鲁翁灭掉了巨龙昆图库塔卡提考特苏瓦西拉松,迎娶了公主米娅莫拉苏娜丹妮谢莉红,国王非常高兴,决定奖励达拉崩吧斑得贝迪卜多比鲁翁任意形状的矩形房子,面积不能超过10^5。达拉崩吧斑得贝迪卜多比鲁翁对国王说:我想要面积为n但周长最小的房子。国王说:好。
Input
输入的第一行也是唯一一行包含整数n(1≤n≤105),其中n是达拉崩吧斑得贝迪卜多比鲁翁房子的面积(平方米)。
Output
以米为单位打印所需达拉崩吧斑得贝迪卜多比鲁翁房子的最小周长。
Examples
Input
36
Output
24
Input
13
Output
28
Input
1
Output
4
Note
在第一个示例中,达拉崩吧斑得贝迪卜多比鲁翁房子的所需形状为6×6平方。其面积为36,周长为6+6+6+6=24。 在第二个示例中,达拉崩吧斑得贝迪卜多比鲁翁房子的所需形状为1×13矩形。其面积为13,周长为1+13+1+13=28。 在第三个示例中,达拉崩吧斑得贝迪卜多比鲁翁房子的所需形状为1×1平方。它的面积是1,周长是1+1+1+1=4。

题解

如果一个矩形的面积确定,那么它越接近正方型,它的周长肯定越短。
所以这题我们找面积的两个 中位因数 就行了,数据不大,直接暴力。
AC代码:

#include <bits/stdc++.h>
using namespace std;
int main(void)
{
    int n;
    cin >> n;
    int a;
    for (int i = 1; i * i <= n; i++)
    {
        if (n % i == 0)
            a = i;
    }
    cout << 2 * (a + (n / a)) << endl;
    return 0;
}

L - 小王蹬三轮

共有N个位置,从左到右依次编号为1,2,3…N.每次给定2个整数a b(a <= b),小王便蹬上三轮车从位置a到位置b依次放一个气球。但是N次以后小王已经忘记了第I个位置已经放过几个气球了,你能帮他算出每个位置分别有几个气球吗?
Input
每个测试实例第一行为一个整数N,(N <= 100000).接下来的N行,每行包括2个整数a b(1 <= a <= b <= N)。
当N = 0,输入结束。
Output
每个测试实例输出一行,包括N个整数,第I个数代表第I个位置的气球的总数。 注意行末不要有多余空格!!!
Sample Input
3
1 1
2 2
3 3
3
1 1
1 2
1 3
0
Sample Output
1 1 1
3 2 1

题解
区间改动 的题目,数据量大,暴力不行,我们可以用 差分数列 ,最后在求差分数列的 前缀和 就行了。
AC代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
int x[maxn];
void updata(int a, int b)
{
    x[a]++, x[b + 1]--;
}
int main(void)
{
    int n;
    while (cin >> n)
    {
        if (n == 0)
            return 0;
        memset(x, 0, sizeof(x));
        int a, b;
        for (int i = 1; i <= n; i++)
        {
            cin >> a >> b;
            updata(a, b);
        }
        int ans = x[1];
        cout << ans;
        for (int i = 2; i <= n; i++)
        {
            ans += x[i];
            cout << ' ' << ans;
        }
        cout << endl;
    }
    return 0;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值