ICPC训练联盟2021寒假冬令营(1)

ICPC训练联盟2021寒假冬令营(1)

A - Specialized Four-Digit Numbers

题目

找到并列出所有具有这样特性的十进制的4位数字:其4位

数字的和等于这个数字以16进制表示时的4位数字的和,也

等于这个数字以12进制表示时的4位数字的和。

• 例如,整数2991的(十进制)4位数字之和是 2+9+9+1 = 21,

因为 2991 = 11728 + 8144 + 9*12 + 3,所以其12进制表示

为189312,4位数字之和也是21。但是2991的十六进制表示

为BAF16,并且11+10+15 = 36,因此2991被程序排除了。

• 下一个数是2992,3种表示的各位数字之和都是22 (包括

BB016),因此2992要被列在输出中。(本题不考虑少于4位

数字的十进制数----排除了前导零----因此2992是第一个正确

答案。)

输入

• 本题没有输入。

输出

• 输出为2992和所有比2922大的满足需求的4位数字(以严格的递

增序列),每个数字一行,数字前后不加空格,以行结束符结束。

输出没有空行。输出的前几行如下所示

2992
2993
2994
2995
2996
2997
2998
2999
...

分析

写一个函数,计算转换成n进制后的各位数字之和。

只需要写一个函数,不用每个进制转换都写一遍。

代码

#include<iostream>
using namespace std;
int fun(int a,int n)
{
    int sum=0;
    int t;
    while(a)
    {
        t=a%n;
        sum+=t;
        a/=n;
    }
    return sum;
}

int main()
{
    for(int i=2992;i<10000;i++)
    {
      if(fun(i,10)==fun(i,16)&&fun(i,10)==fun(i,12))
            cout<<i<<endl;
    }
   return 0;
}

B - Pig-Latin

题目

您意识到PGP加密系统还不足够保护您的电子邮件,

所以,你决定在使用PGP加密系统之前,先把您的

明文字母转换成Pig Latin(一种隐语),以完善加

密。

输入和输出

• 请您编写一个程序,输入任意数量行的文本,并以Pig Latin输出。

每行文本将包含一个或多个单词。一个“单词”被定义为一个连

续的字母序列(大写字母和/或小写字母)。单词根据以下的规则

转换为Pig Latin,非单词的字符在输出时则和输入中出现的完全

一样:

• [1] 以元音字母(a、e、i、o或u,以及这些字母的大写形式)开

头的单词,要在其后面附加字符串“ay”(不包括双引号)。例

如,“apple”变成“appleay”。

• [2] 以辅音字母(不是A, a, E, e, I, i, O, o, U 或 u的任何字母)开头

的单词,要去掉第一个辅音字母,并将之附加在单词的末尾,然

后再在单词的末尾加上“ay”。例如,“hello”变成“ellohay”。

• [3] 不要改变任何字母的大小写

输入

This is the input.

输出

hisTay isay hetay inputay.

分析

元音字母单词后面直接加ay,辅以字母开头单词,把第一个辅音字母换到单词末尾再加ay,其他的不变

C++字符判断函数,cctype头文件中的函数

isalpha(a),如果a是字母,函数返回true;

isdigit(a),如果a是数字,函数返回true;

isalnum(a),如果a是字母或数字,函数返回true;

isupper(a),如果a是大写字母,函数返回true;

islower(a),如果a是小写字母,函数返回true;

tolower(a),如果a是大写字母,返回其小写,否则返回a

toupper(a),如果a是小写字母,返回其大写,否则返回a

代码

#include<iostream>
#include<string>
#include<cctype>
using namespace std;
bool judge(char ch)//判断元音
{
    ch=toupper(ch);
    if(ch=='A'||ch=='E'||ch=='I'||ch=='O'||ch=='U')
        return true;
    return false;
}
int main()
{
    string s;
    while(getline(cin,s))//读入一行
    {
        int len=s.size();
        for(int i=0;i<len;i++)//分析每一行
        {
            if(isalpha(s[i]))//如果是字母
            {
                //是元音字母
                 if(judge(s[i]))
                 {
                     while(isalpha(s[i]))
                     {
                         cout<<s[i];
                         i++;
                     }
                     cout<<"ay";
                     i--;
                 }
                 else//是辅音字母
                 {
                     char t=s[i];
                     i++;
                     while(isalpha(s[i]))
                     {
                         cout<<s[i];
                         i++;
                     }
                     cout<<t<<"ay";
                     i--;
                 }
            }
            else cout<<s[i];
        }
        cout<<endl;
    }
}

C - Tic Tac Toe

题目

在这里插入图片描述

输入

• 输入的第一行给出N,表示测试用例的数目。然后给出

4N-1行,说明N个用空行分隔的网格图。

输出

• 对于每个测试用例,在一行中输出"yes"或"no",表示该

网格图是否是有效的三连棋游戏的一个步骤

Sample Input
2
X.O
OO.
XXX

O.X
XX.
OOO
Sample Output
yes
no

分析

在这里插入图片描述

分析多种情况。

因为玩家X先走,所以O数目小于等于X数目

X数目不会比O数目多2个

玩家O和X不能同时判赢

玩家O赢了,O的个数必须等于X个数

玩家X赢了,O的个数必须小于X个数

所以只要不满足上面五个条件之一,就是无效的

要写判断赢的函数,一行,一列,主对角线,副对角线逐个分析。

代码

#include<iostream>
#include<string>
#include<cctype>
using namespace std;
char plant[4][4];
int i,j;
bool win(char c)//判断谁赢的函数
{
    for(i=0;i<3;i++)
    {
        for(j=0;j<3&&plant[i][j]==c;j++)//判断一行是否相同
            if(j==2)
                return true;
        for(j=0;j<3&&plant[j][i]==c;j++)//判断一列是否相同
            if(j==2)
                return true;
    }
    for(i=0;i<3&&plant[i][i]==c;i++)//判断主对角线是否相同
        if(i==2)
            return true;
    for(i=0;i<3&&plant[i][2-i]==c;i++)//判断副对角线是否相同
        if(i==2)
            return true;
    return false;
}
int main()
{
    int flag;
    int n,xcount,ocount;
    cin>>n;
    getchar();
    while(n--)
    {
        xcount=0;
        ocount=0;
        for(i=0;i<3;i++)//输入网格数据
            cin>>plant[i];
        flag=1;
        for(i=0;i<3;i++)//计算x和O出现的次数
        {
            for(j=0;j<3;j++)
            {
                if(plant[i][j]=='X')
                    xcount++;
                else if(plant[i][j]=='O')
                    ocount++;
            }
        }
        if(win('X')&&win('O'))//两个人同时赢了
            flag=0;
        if(win('X')&&xcount==ocount)//X赢了,但双方棋子一样多
            flag=0;
        if(ocount>xcount||xcount-ocount>1)//O个数大于X或者x比O多于1
            flag=0;
        if(win('O')&&ocount!=xcount)//O赢了,但是双方棋子不等
            flag=0;
        if(win('X')&&ocount==xcount)//X赢,但是双方棋子个数相同
            flag=0;
        if(flag)
            cout<<"yes"<<endl;
        else
            cout<<"no"<<endl;
    }
    return 0;
}

D - Factorial! You Must be Kidding!!!

题目

在这里插入图片描述

输入

• 输入包含若干行,每行给出一个整数n。不会有整数超过6位。输

入以EOF结束。

输出

• 对于每一行的输入,输出一行。如果n!的值在Arif 计算机的无符

号长整数范围内,输出行给出n!的值;否则输出行给出如下两行

之一:

Overflow!

//(当 n! > 6227020800)

Underflow!

//(当 n! < 10000)

Sample Input
2
10
100
Sample Output
Underflow!
3628800
Overflow!

分析

在这里插入图片描述

首先,离线计算F[i]=i!,8≤i≤13。

• 然后,对每个n

• 如果8≤n≤13,则输出F[n];

• 如果(n>=14||(n<0&&(-n)%2==1)),则输出"Overflow!";

• 如果(n<=7 || (n<0&&(-n)%2==0)),则输出"Underflow!"。

• 在参考程序中,函数init()离线计算F[i]=i!,0≤i≤13。在

函数中,循环变量i为局部变量;而F[i]则为全局变量。

递归问题打表

递归容易栈溢出,用循环不用递归来打表,算一下题目要求的数字,不能直接用递归来做

代码

#include<iostream>
#include<string>
using namespace std;
typedef long long ll;
/*
打表,先离线计算F[i]=i!,8<=i<=13;
对每个n
8<=n<=13,输出F[n];
n>=14||(n<0&&(-n)%2==1),Overflow!
n<=7||(n<0&&(-n)%2==0),Underflow!
n=7时,n!=
*/
//递归函数
/*
int fac(int n)
{
    if(n==0)
        return 1;//递归边界
    if(n>=1)
        return fac(n)*fac(n-1);
}
*/
//n=13时,13!=6227020800
const int N=13;
const long long Min=10000,Max=6227020800;
long long F[N+1];//定义全局变量,用于打表
void init()
{
    F[0]=1;
    for(int i=1;i<=N;i++)
        F[i]=i*F[i-1];
}
int main()
{
    init();
    int n;
    while(cin>>n)
    {
        if(n>N||(n<0&&(-n)%2==1))
            cout<<"Overflow!"<<endl;
        else if(F[n]<Min||(n<0&&(-n)%2==0))
            cout<<"Underflow!"<<endl;
        else
            cout<<F[n]<<endl;
    }
    return 0;
}

E - Function Run Fun

题目

我们都爱递归!不是吗?

• 请考虑一个三参数的递归函数w(a, b, c):

• if a <=0 or b <= 0 or c <=0, then w(a, b, c) 返回1;

• if a > 20 or b > 20 or c > 20, then w(a, b, c) 返回w(20, 20, 20);

• if a < b and b < c, then w(a, b, c) 返回w(a, b, c-1)+w(a, b-1, c-1)- w(a, b-1, c);

• 否则,返回w(a-1, b, c) + w(a-1, b-1, c) + w(a-1, b, c-1) - w(a-1, b-1, c-1)。

• 这是一个很容易实现的函数。但存在的问题是,如果直接实现,

对于取中间值的abc(例如,a=15,b=15,c=15),由于大

量递归,程序运行非常耗时

输入

• 程序的输入是一系列整数三元组,每行一个,一直到结束标志-1-

1-1为止。请您高效地计算w(a, b, c)并输出结果。

输出

• 输出每个三元组w(a, b, c)的值

Sample Input
1 1 1
2 2 2
10 4 6
50 50 50
-1 7 18
-1 -1 -1
Sample Output
w(1, 1, 1) = 2
w(2, 2, 2) = 4
w(10, 4, 6) = 523
w(50, 50, 50) = 1048576
w(-1, 7, 18) = 1

分析

对于取中间值的abc,由于大量递归,程

序运行非常耗时。所以,本题的递归函数计

算采用记忆化递归进行计算,用一个三维的

数组f来记忆递归的结果,f[a] [b] [c]用于记忆

w(a, b, c)的返回值

记忆化递归

用一个数组存下递归结果,不是直接的递归

代码

#include<iostream>
#include<string>
#include<cstring>
using namespace std;
typedef long long ll;
//记忆化递归
const int N=20;
int f[N+1][N+1][N+1];
int w(int a,int b,int c)
{
   /* if(a<=0||b<=0||c<=0)
        return 1;
    else if(a>20||b>20||c>20)
        return w(20,20,20);
    else if(a<b&&b<c)
        return w(a, b, c-1)+w(a, b-1, c-1)- w(a, b-1, c);
    else return w(a-1, b, c) + w(a-1, b-1, c) + w(a-1, b, c-1) - w(a-1, b-1, c-1);
*/
    if(a<=0||b<=0||c<=0)
        return 1;
     else if(a>N||b>N||c>N)
        return w(N,N,N);
     else if(f[a][b][c])
        return f[a][b][c];//f[a][b][c]记忆w(a,b,c)
    else if(a<b&&b<c)
        return f[a][b][c]=w(a, b, c-1)+w(a, b-1, c-1)- w(a, b-1, c);
    else return f[a][b][c]=w(a-1, b, c) + w(a-1, b-1, c) + w(a-1, b, c-1) - w(a-1, b-1, c-1);

}
int main()
{
    memset(f,0,sizeof(f));//三维数组初始化
    int a,b,c;
    while(cin>>a>>b>>c)
    {
        if(a==-1&&b==-1&&c==-1)
            return 0;
        else
            cout<<"w("<<a<<", "<<b<<", "<<c<<") = "<<w(a,b,c)<<endl;
    }
    return 0;
}

F - Simple Addition

题目

在这里插入图片描述

输入

• 输入包含若干行。每行给出两个非负整数pqpq),这两个

整数之间用一个空格隔开,pq是32位有符号整数。输入由包含

两个负整数的行结束。程序不用处理这一行。

输出

• 对于每行输入,输出一行,给出S(p, q)的值。

Sample Input
1 10
10 20
30 40
-1 -1
Sample Output
46
48
52

分析

在这里插入图片描述

本题递归算法:

•每一轮求出[p, q]区间内数字的个位数的和,并

计入总和;再将[p, q]区间内个位数为0的数除以

10,产生新区间,进入下一轮,再求新区间内数

字的个位数的和,并计入总和,而个位数为0的

数除以10,产生新区间;以此类推;直到[p, q]

区间内的数只有个位数

• 例如,求S(2, 53),将范围划分为3个区间:[2, 9],[10, 50]和[51,

53]。

• 对于第1个区间[2, 9],个位数之和2+3+4+……+9=44;对于第2个区

间[10, 50],个位数之和(1+2+……+9)4= 454 = 180;对于第3个区

间[51, 53],个位数之和1+2+3 = 6。所以,第一轮,个位数的总和

为44+180+6 = 230。

• 在[10, 50]中,10, 20, 30, 40和50的个位数是0,将这些数除以10后

得1, 2, 3, 4, 5,产生新区间[1, 5];进入第二轮,区间[1, 5]中的数

只有个位数,个位数之和1+2+3+4+5=15。

• 最后,两轮结果相加,得S(2, 53)=230+15=245。

递归分而治之优化

直接相加会超时,要分析题意

代码

#include<iostream>
#include<string>
#include<cstring>
using namespace std;
typedef long long ll;
//直接累加会超时,要进行优化
ll ans;
ll f(ll n)
{
    if(n%10>0)
        return n%10;
    else if(n==0)
        return 0;
    else return f(n/10);
}
void solve(ll p,ll q)//计算s(p,q)
{
    if(q-p<9)//如果q-p<9直接计算
    {
        for(int i=p;i<=q;i++)
            ans+=f(i);
        return;
    }
    while(p%10)//第一个区间,计算个位数之和
    {
        ans+=f(p);
        p++;
    }
    while(q%10)//第三个区间,计算个位数之和
    {
        ans+=f(q);
        q--;
    }
    ans+=45*(q-p)/10;//第2个区间,计算个位数之和
    solve(p/10,q/10);//递归进入下一轮
}
/*
int s(int p,int q)
{
    for(int i=p;i<=q;i++)
    {
        sum+=f(i);
    }
}
*/
int main()
{
    ll p,q;
    while(cin>>p>>q)
    {
        ans=0;
        if(p<0&&q<0)
            break;
        solve(p,q);
        cout<<ans<<endl;
    }
}

G - A Contesting Decision

题目

对程序设计竞赛进行裁判是一项艰苦的工作,要面对要求严格的

参赛选手,要作出乏味的决定,以及要进行着单调的工作。不过,

这其中也可以有很多的乐趣。

• 对于程序设计竞赛的裁判来说,用软件使得评测过程自动化是一

个很大的帮助,而一些比赛软件存在的不可靠也得使人们希望比

赛软件能够更好、更可用。您是竞赛管理软件开发团队中的一员。

基于模块化设计原则,您所开发模块的功能是为参加程序设计竞

赛的队伍计算分数并确定冠军。给出参赛队伍在比赛中的情况,

确定比赛的冠军。

记分规则:

• 一支参赛队的记分由两个部分组成:第一部分是被解出的题数,

第二部分是罚时,表示解题总的耗费时间和试题没有被解出前错

误的提交所另加的罚时。对于每个被正确解出的试题,罚时等于

该问题被解出的时间加上每次错误提交的20分钟罚时。在问题没

有被解出前不加罚时。

• 因此,如果一支队伍在比赛20分钟的时候在第二次提交解出第1

题,他们的罚时是40分种。如果他们提交第2题3次,但没有解决

这个问题,则没有罚时。如果他们在120分钟提交第3题,并一次

解出的话,该题的罚时是120分。这样,该队的成绩是罚时160分,

解出了两道试题。

• 冠军队是解出最多试题的队伍。如果两队在解题数上打成平手,

那么罚时少的队是冠军队

输入

• 程序评判的程序设计竞赛有4题。本题设定,在计算罚时后,不会导致

队与队之间不分胜负的情况。

• 第1行 为参赛队数n

• 第2 - n+1行为每个队的参赛情况。每行的格式

• < Name > < p1Sub > < p1Time > < p2Sub > < p2Time > … < p4Time >

• 第一个元素是不含空格的队名。后面是对于4道试题的解题情况(该队

对这一试题的提交次数和正确解出该题的时间(都是整数))。如果

没有解出该题,则解题时间为0。如果一道试题被解出,提交次数至少

是一次。

输出

• 输出一行。给出优胜队的队名,解出题目的数量,以及罚时。

Sample Input
4
Stars 2 20 5 0 4 190 3 220
Rockets 5 180 1 0 2 0 3 100
Penguins 1 15 3 120 1 300 4 0
Marsupials 9 0 3 100 2 220 3 80
Sample Output
Penguins 3 475

分析

简单结构体排序,也可在处理过程中进行比较,记下队名,题数,罚时

代码

#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
typedef struct
{
    string name;
    int sub[5];
    int time[5];
    int sumnum;
    int sumtime;
}team;
team t[100010];
bool cmp(team a,team b)
{
    if(a.sumnum==b.sumnum)
        return a.sumtime<b.sumtime;
    else
        return a.sumnum>b.sumnum;
}
int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>t[i].name;
        t[i].sumnum=0;
        t[i].sumtime=0;
        for(int j=0;j<4;j++)
        {
            cin>>t[i].sub[j]>>t[i].time[j];
            if(t[i].time[j]!=0)
            {
                t[i].sumnum++;
                t[i].sumtime=t[i].sumtime+t[i].time[j]+20*(t[i].sub[j]-1);
            }
        }
    }
    sort(t,t+n,cmp);
    cout<<t[0].name<<" "<<t[0].sumnum<<" "<<t[0].sumtime;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HHHᕙ(`▿´)ᕗ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值