2020-2021年度第二届全国大学生算法设计与编程挑战赛(冬季赛)题解

热身赛

排列巨人

题目描述:

海的那边是敌人!

为了夺回自由,艾尔迪亚帝国开始筹备起帝国巨人军队,利用艾伦始祖巨人之力,来指挥军队征战。

现在有12名巨人,他们的个子非常奇怪,第ii名巨人的身高为ii米。

现在,艾伦要将这12名巨人排成一排。

他想知道这12名巨人的排列方式有多少种。

例如:对于3名巨人的排列方式有6种:{1,2,3}、{1,3,2}、{2,3,1}、{2,1,3}、{3,1,2}、{3,2,1}

请输出12名巨人的排列方式有多少种。

题意:

输出12的全排列的个数

思路1:

使用全排列秘技:next_permutation

#include<bits/stdc++.h>
using namespace std;
int main(){
    int sum = 1;
    int tr[] = {1,2,3,4,5,6,7,8,9,10,11,12};
    while (next_permutation(tr, tr + 12)) {
        sum++;
    }
    cout<<sum<<endl;   
}

思路2:

其实就是高中的排列组合而已,A(n,n)

#include<bits/stdc++.h>
using namespace std;
int main(){
    int sum = 1;
    for(int i = 1; i <= 12; ++i)
    sum *= i;
    cout<<sum<<endl;   
}

三子棋

题目描述:

在遥远的卡拉迪亚大陆,人们喜好骑马喜好砍杀。

大陆征战向来血雨腥风,但为了社会主义的和谐发展,我们可以通过三子棋来决胜负!

已知棋盘规模为 S*T ,具体参加三子棋的人数并不固定,现在你开了上帝之眼,你能看出来,究竟是谁赢下来这场「血腥」三子棋吗?

胜利条件为:某一行或某一列或某一个斜方向(从左上到右下斜方向或从右上到左下斜方向)上有连续至少三个相同的棋子,则这枚棋子为获胜玩家的棋子。

题目保证最多只有一名玩家获胜。

题意:

给你张地图,问你有没有出现连续三个相同的棋子的情况

思路:

nm最大30,可以直接暴力,对每个点进行判断,就四个方向挨个判断即可

#include<bits/stdc++.h>
using namespace std;
int n, m;
char tr[35][35];

bool judge(int x, int y){
    if(tr[x][y] == '.')return false;
    else {
        if((tr[x][y] == tr[x - 1][y] && tr[x][y] == tr[x + 1][y]) || (tr[x][y] == tr[x][y - 1] && tr[x][y] == tr[x][y + 1]) || (tr[x][y] == tr[x - 1][y - 1] && tr[x][y] == tr[x + 1][y + 1]) || (tr[x][y] == tr[x - 1][y + 1] && tr[x][y] == tr[x + 1][y - 1]))
            return true;
    }
    return false;
}

int main(){
    cin>>n>>m;
    for(int i = 1; i <= n; ++i)for(int j = 1; j <= m; ++j)scanf(" %c",&tr[i][j]);
    for(int i = 1; i <= n; ++i)
    for(int j = 1; j <= m; ++j){
        if(judge(i, j) == true){
            cout<<tr[i][j]<<endl;
            return 0;
        }
    }
    cout<<"ADPC!
";
    return 0;
}

钻石

题目描述:

您是一个神仙, 有一天也想体验人间的快乐,于是来到市中心最大的商圈购物。

钻石闪亮而耀眼,您决定要买几个带回去分给好兄弟们。

具体而言,商场中有N颗钻石正在贩卖,第ii颗钻石的体积为Ai,其价格为Si

您的手上有M个购物袋。因为是送给不同的神仙,所以每个购物袋只能装一个钻石。同时,购物袋大小不一,第i个袋子可以装入钻石的最大体积为Ri。

神仙资金都十分充足,所以您要使最终买下的钻石总价格尽可能大,请问最大总价格是多少?

思路:

用map存购物袋的体积和个数,用结构体存钻石的价格和体积

对钻石从高到低进行排序,然后对所有的钻石,都去map里面lowerbound一下,也就是对于每个钻石,我们去找大于他体积的袋子,如果找到了就更新答案,如果没找到就进行下一个钻石的查找

特别要注意,如果购物袋的数量为0了,就得删掉!

#include<bits/stdc++.h>
using namespace std;
int n, m, r;
ll sum;
map<int, int>mp;
map<int, int>::iterator it;//迭代器,用来接受lowerbound的值
struct ran{
    int a, s;
}tr[MAX];

bool cmp(ran x, ran y){
    return x.s > y.s;
}//按价格降序排序

int main(){
    cin>>n>>m;
    for(int i = 1; i <= n; ++i){
        tr[i].a = IntRead();tr[i].s = IntRead();
    }
    sort(tr + 1, tr + 1 + n, cmp);
    for(int i = 1; i <= m; ++i){
        r = IntRead();
        mp[r]++;
    }
    for(int i = 1; i <= n; ++i){//对所有的购物袋进行查找
        it = mp.lower_bound(tr[i].a);
        if(it != mp.end()){//找到了就更新答案
            sum += tr[i].s;
            mp[it -> first]--;
        }
      //如果数量为0了,就得删掉,否则会影响接下来的查找
        if(mp[it -> first] == 0)mp.erase(it -> first);       
    }
    cout<<sum<<endl;
  	return 0;
}

正式赛

你在最后那场博弈中败下阵来,却意外穿越到了海拉尔大陆!是你吗林克?

初来到海拉尔大陆的你,有些许的局促,但当你看到,或许一切的一切都迎刃而解。

一个层高为n的字母塔的定义为:

  • 共n行,由字母组成的等腰三角形。
  • 塔顶为第一层,且只有一个大写字母A;下面每一层都比上面一层多两个字母。
  • 每一层都是左右对称
  • 对于第i层,前i个字母由大写字母表中A~第i个字母顺序组成。

为了稳住局面,样例给出了层高为5的字母塔,请你输出层高26的字母塔

#include<bits/stdc++.h>//有手就行系列
using namespace std;
int main(){
    for(int i = 1; i <= 26; ++i){
        for(int j = 1; j <= 26 - i; ++j)cout<<' ';
        for(int j = 1; j <= i; ++j)cout<<(char)('A' + j - 1);
        for(int j = i - 1; j >= 1; --j)cout<<(char)('A' + j - 1);
        cout<<endl;
    }
}

日记

题目描述:

看着林林色色的塔,你的心里有些许的安稳,在询问路人时你得知了,你正身处「卡卡利科村」,似乎帕雅也在那里?

好久没有偷窥帕雅的日记了

你喜欢偷窥帕雅日记一事已广为人知,帕雅特地在日记本上加了密。

加密的方式很简单:对于一串字符串,如果其中有linke这五个字母当中的任意一个,帕雅都会在这后面加上bt再加上原来的字母已加密,如love就会加密成lbtlovebte

下面给出帕雅日记的第一页内容,请你根据他的日记内容进行解密。

ibti lbtlovebte lbtlibtinbtnkbtkebtezbas jebte dosadnbtna ovakbtkebtemibtijaxaszxdbtddbtddbtddbtddbtddbtd

注意上面内容为一行内容,没有任何换行,若网页显示多行只是文本显示宽度问题。

建议查看PDF。

但这能拦得住你吗?时间紧迫,快解密吧!

思路:

可以直接手解,也可以写代码来解,不过手算的话注意最后面的是d,不属于l,i,n,k,所以得抄下来,不能解密成d(艹,我就没看见,直接wa 4,最后写了个程序解决(#゚Д゚))

#include<bits/stdc++.h>//有手就行系列
using namespace std;
int main(){
    cout<<"i love linkezbas je dosadna ovakemijaxaszxdbtddbtddbtddbtddbtddbtd
";
    return 0;
}

神仙爱采药

题目描述:

您是一个神仙,但您很喜欢采药。

您有一个神奇的背包,背包内有VV个格子。

您所在的空间内有一些药,每个药会占用 11 或 22 个格子。

每天可以进行一次如下操作:

采摘一个药材放入背包中,若此时背包中没有多余的格子来放入新的药材,可以先将背包中的若干药材扔出去,至于扔多少以及扔几个,全都由您决定。当然您也可以选择不去进行采摘操作。

每一天结束前,神奇背包中的每个药材都会产生一个药丸。

作为神仙,您知道每天您可以采摘的药材类型(即占用格子数目),注意,当天的药材如果不采摘,在第二天就会消失(当天药材仅限当天采摘)。

为了获得尽可能多的药丸,请您计算最终能获得的药丸数目最多是多少?

思路:

有格子的时候,看见什么就塞什么,没有格子的时候遇到1就把2扔出去,再把1塞进去

#include<bits/stdc++.h>//有手就行系列
using namespace std;
ll v,m, sum;//m代表剩余体积,num表示数量
string s;
ll tr[3];
int main(){
    cin>>v>>s;
    m = v;
    for(int i = 0; i < s.size(); ++i){
        if(s[i] == '1'){
            if(m >= 1){
                m--;tr[1]++;
            }
            else if(m < 1 && tr[2] > 0){
                tr[2]--;tr[1]++;
                m++;//把2扔出去记得给体积加1!
            }
        }
        else if(s[i] == '2'){
            if(m >= 2){
                m-=2;tr[2]++;
            }
        }
        sum += tr[1] + tr[2];
    }
    cout<<sum<<endl;
    return 0;
}

奇怪的传输机增加了

题目描述:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HhVbJcmC-1615809049941)(/Users/chelsea/Library/Application Support/typora-user-images/image-20210315192821713.png)]

思路:

简单的模拟,文字游戏!

#include<bits/stdc++.h>//有手就行系列
using namespace std;
int  x, y, cnt;
double ans, m, n;
int main(){
    cnt = 1;//记录能否成功
    cin>>n>>x>>y;
    ans = n;
    m = ans / 32;
    for(int i = 1; i <= y; ++i){
        ans = ans * 2 / 3;
        if(i == x){//到第x天加数据量
            ans = ans >= n / 2 ? n : ans + n / 2;
        }
        if(ans < m){//判输
            cout<<"N0!
"<<i<<' ';
            printf("%.6lf
",ans);
            cnt = 0;
            break;
        }
    }
    if(cnt){
        cout<<"YE5!
";
        printf("%.6lf
",ans);
    }
    return 0;
}

奇怪的小鸭子也增加了

题目描述:

这题是个签到题。

有一个 A×B 的大澡盆,还有若干个a×b的长方形小鸭子,澡盆里最少放几只鸭子后,便无法再向其中放入更多的鸭子?

鸭子很倔强,不能旋转成 b * a ,也不能重叠放置

思路:

将A分成两大部分,第二部分是A % (2 * a),剩下的就是第一部分。第一部分,可以继续分成k个2*a的小块,每个小块当一只鸭子,对于这k个小鸭子,是没法再放鸭子的,再剩下的第二部分,如果大于等于a,则还可以放一个鸭子,另一方向同理。

这里采用了的是近似的原则,因为地方要是小于0.000001,鸭子就进不去,更何况还可以比0.000001小好多好多倍,就极限成了0,也就是一个鸭子占俩位置

#include<bits/stdc++.h>//有手就行系列
using namespace std;
int A, B, a, b;
int aa, bb;
int main(){
    cin>>A>>B>>a>>b;
    aa = A / (2 * a);
    if(A %(2 * a) >= a)aa++;
    bb = B / (2 * b);
    if(B % (2 * b) >= b)bb++;
    cout<<1ll * aa * bb<<endl;
    return 0;
}

关于哥俩好的数字这件事

题目描述:

数字 x,yx,y 是个「哥俩好」数字,当且仅当数字 xx 的数位和与数字 yy 的数位和相同。

你需要找 nn 个不同的正整数,使得这 nn 个数字两两之间均为「哥俩好」数字且总和最小。

思路:

打个暴力,根据打表可以发现,到600000的时候就可以找到5000的答案

用一个数组cnt[i]来表示数位和为i的元素的个数,用tr[i]来表示数位和为i的总和,边循环边更新这连两个数组的值,遇到cnt = n的时候就更新答案,最后输出即可

这个题到最后也没做出来,是因为我暴力的方法记录的东西比较多,就爆栈了,然后就开始找规律,结果发现没有规律!orz

#include<bits/stdc++.h>
using namespace std;
ll n, m, minx;
ll cnt[100],tr[50];
ll f(ll x){
    ll sum = 0;
    while (x) {
        sum += x % 10;
        x /= 10;
    }
    return sum;
}
int main(){
    cin>>n;
    minx = 1e18;
    for(int i = 1; i <= MAX; ++i){
        cnt[f(i)]++;
        tr[f(i)] += i;
        if(cnt[f(i)] == n){
            minx = min(minx, tr[f(i)]);
        }
    }
    cout<<minx<<endl;
    return 0;
}

出题人说这道题是一个签到题

题目描述:

比赛前一天,出题人接到了一个电话…

我们都知道2020-2021年度第二届全国大学生算法设计与编程挑战赛(冬季赛)是由中国未来研究会大数据与数学模型专业委员会举办的面向全国高校大学生的学科竞赛,旨在激发学生学习计算机领域专业知识与技能的兴趣,鼓励学生主动灵活地运用计算机知识和技能解决实际问题,有效提升算法设计、逻辑推理、数学建模、编程实现和计算机系统能力,培养团队合作意识、挑战精神和创新能力。

主办单位:全国大学生算法设计与编程挑战赛组委会、中国未来研究会大数据与数学模型专业委员会。

现在,出题人想测试一下你的编程能力!

出题人有一个人工智能的项目要做,大概是问答相关。但因为明天就要检验了,今天只来得及「新建文件夹」啦。

快帮帮他!他要做一个自动回复的脚本,功能如下:

如果输入的值是1,则回复ADPC

否则的话,输出12345

你需要帮助出题人完成这个自动回复的脚本。

思路:

水题不解释

#include<bits/stdc++.h>
using namespace std;
int n;
int main(){
    cin>>n;
    if(n == 1)cout<<"ADPC
";
    else cout<<"12345
";
    return 0;
}

还有一个字符串的大模拟(谁写谁后悔的那种)
再就剩下5道金牌题???
除了签到就是金???就尼玛离谱

不是所有的牛奶都叫特仑苏,也不是所有的人都叫猪猪
原文地址:https://www.cnblogs.com/chelsea0901/p/14539625.html