2020年湖南中医药大学“华为杯”大学生程序设计竞赛(赛后补题)

这次院赛给打自闭了,从头到尾一直落在后面,最后只拿到了二等奖尾,还是太菜了,唉(难受)。多刷题!!! (未完待续)

A:幸福小组

题目描述
俗话说:男女搭配,干活不累。还据说,如果在做一件事情时男生和女生人数相等,学习和工作效率可以达到最佳【瞎说的】。
为了更好地学习计算机专业知识,某个班分为n个学习小组,如果某一个组中男生和女生人数相等,这样的小组称为“幸福小组”。
现在分别给出这n个小组中男生和女生人数,请你编写一个程序统计一共有多少个“幸福小组”。

输入
单组输入。第1行输入一个正整数n,表示学习小组数量。(n<=100)
接下来n行每行包含两个正整数,分别表示每一个学习小组中男生人数和女生人数,两个数字之间用空格隔开。

输出
“幸福小组”的个数。

样例输入 Copy

4
1 5
5 1
3 3
4 4

样例输出

2

分析:签到题,计算输入的学习小组中男女生人数相等的小组数有几个。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <vector>

using namespace std;

int main()
{
    int n,a,b,sum=0;
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>a>>b;
        if(a==b)sum++;
    }
    cout<<sum<<endl;
    return 0;
}

B:菜鸟驿站

题目描述
HNUCM的TC同学在学校开设了一个收发快递的“菜鸡驿站”,HNUCM一共有n栋宿舍楼,TC需要逐一把快递送到每一栋宿舍楼。
因为刚过完双十一,快递实在是太多太多了,TC每一次只能送一栋宿舍楼,然后再返回驿站取快递来送到下一栋宿舍楼。
现在给出驿站到每一栋宿舍楼的单程时间(单位:分钟,假设来回所需的时间相等),请问TC送完所有快递并返回驿站一共需要多少分钟的时间?

输入
单组输入,每组第1行输入一个正整数n表示宿舍楼的数量(n<=50)。
第2行输入n个正整数分别表示驿站到每一栋宿舍楼所需时间T(T<=109)(单位:分钟)。

输出
送完所有快递并返回到驿站所需的总时间(分钟)。

样例输入

5
3 4 5 6 7

样例输出

50

分析:签到题,将单程时间相加乘以2即是全程时间,注意开long long型。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <vector>

using namespace std;

int main()
{
    int n;
    long long int t,sum=0;
    cin>>n;
    for(int i=0;i<n;i++)cin>>t,sum+=t;
    cout<<sum*2<<endl;
    return 0;
}

C:TC的火柴

题目描述
今天,TC买了一盒火柴,打算和小yh比拼谁能用有限的火柴得到一个最大数字。火柴拼数字规则是,拼出零到九分别需要6、2、5、5、4、5、6、3、7、6个火柴。
现在给TC a个不同火柴数,求你帮TC计算不同的火柴数a分别能组成的最大数字,如果不能组成数字,输出-1。

输入
第一行输入一个n。
接下来n行,每行一个数a(0<=a<=10^3)

输出
每行输出一个能组成的最大数字。

样例输入

3
1
2
4

样例输出 Copy

-1
1
11

提示
TC说火柴要省着用

分析:思维题,贪心。贪心策略:如果a小于2,那么无法组成数字,输出-1。否则,我们优先考虑需要火柴最少的数字1。如果a除以2有余数,那么最后会剩下一个火柴,将这个火柴和第一个1一起可以组成需要三个火柴的7,剩下的火柴构成(a-3)/2个1。否则,所有火柴构成a/2个1.

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <vector>
using namespace std;

int main()
{
    int n,a;
    cin>>n;
    while(n--){
        cin>>a;
        int ans=a/2;
        if(ans==0){
            cout<<-1<<endl;
            continue;
        }
        if(a%2==1)cout<<7;//最终将剩余一根火柴,那么可以第一个1可以加上这一根变成7
        else cout<<1;
        for(int i=0;i<ans-1;i++)cout<<1;
        cout<<endl;
    }
    return 0;
}

D:n盏灯

题目描述
有n盏灯,序号为1到n。在初始状态下,所有的开关都是关的。
有n个开关控制这n盏灯,第i个开关控制第i盏灯。
现在从1开始计数,每遇到一个素数就把该素数对应的倍数序号的开关朝相反的方向拨一下,一直到最后一个小于等于n的素数为止。请统计最后有多少盏灯是开的?

输入
多组输入,每组输入一个n(2<=n<=106)。

输出
每组数据,输出开的灯的盏数

样例输入

10

样例输出

7

分析:先预处理所有的素数。然后从1开始模拟,遇到素数就取其所有的倍数,从而记录每一盏灯的开关次数。详细见代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <vector>

using namespace std;
const int maxn=1000000+5;
bool prime[maxn];
int sum[maxn];
bool judge(int n){
    if(n==1)return false;
    if(n<=3)return n>1;
    if(n%2==0||n%3==0)return false;
    for(int i=5;i<=sqrt(n);i+=6){
        if(n%i==0||n%(i+2)==0)return false;
    }
    return true;
}//六素数法
void init(){
    for(int i=1;i<=maxn;i++)
        if(judge(i))prime[i]=true;
}
int main()
{
    int n;
    init();
    while(~scanf("%d",&n)){
        int ans=0;
        memset(sum,0,sizeof(sum));//置0函数
        for(int i=1;i<=n;i++){
            if(!prime[i])continue;
            for(int j=i;j<=n;j+=i)sum[j]++;
        }
        for(int i=1;i<=n;i++)
            if(sum[i]%2==1)ans++;
        cout<<ans<<endl;
    }
    return 0;
}

E:电子手表

题目描述
一个电子手表,可以显示时分秒,且每一个都采用两位阿拉伯数字。
现在输入一个开始时间和一个结束时间,例如00:00:00和10:20:59。
请统计从开始时间到结束时间(包括开始时间和结束时间)之间在屏幕上显示0的个数。
注意:每秒钟为1个计数单位,例如00:00:00到00:00:01一共显示了11个0。
输入保证开始时间和结束时间的合法性,即0<=小时<=23,0<=分钟<=59,0<=秒钟<=59。

输入
开始时间和结束时间,格式均为HH:MM:SS(HH表示小时,MM表示分钟,SS表示秒钟)。

输出
从开始时间到结束时间之间在屏幕上显示0的个数(包括开始时间和结束时间)。

样例输入

00:00:00
00:00:10

样例输出

56

分析:一道模拟题,比赛的时候我竟然没有做出来,我真的要哭死了,赛后一发过。其实就是模拟时间的变化,计算每一秒钟的0的个数。(详细见代码)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <vector>
#include <set>

using namespace std;
int cou(int t){
    if(t==0)return 2;
    else if(t<10)return 1;
    else if(t%10==0)return 1;
    return 0;
}
int dis(int h,int m,int s){
    return cou(h)+cou(m)+cou(s);
}
int main()
{
    int h1,m1,s1,h2,m2,s2,sum=0;
    scanf("%d:%d:%d %d:%d:%d",&h1,&m1,&s1,&h2,&m2,&s2);
    while(h1<h2){//模拟时间的变化
        while(m1<60)//分钟到60时变为0
            while(s1<60){//秒种到60时变为0
                sum+=dis(h1,m1,s1);//每一秒都计算有多少个0
                s1++;
            }
            s1=0;
            m1++;
        }
        m1=0;
        h1++;
    }
    while(m1<m2){//计算最后m2分钟的0的个数
        while(s1<60){
            sum+=dis(h1,m1,s1);
            s1++;
        }
        s1=0;
        m1++;
    }
    while(s1<=s2)sum+=dis(h1,m1,s1),s1++;//计算最后s2秒的0的个数
    cout<<sum<<endl;
    return 0;
}

F:TC的steam账号

题目描述
一共有n个学弟(编号为1-n)向TC借steam的账号,不过TC休闲时也会玩下steam游戏,所以TC准备只借给其中的k个人。

TC按照他们的OJ刷题数量给每个人赋予了一个初始权值W[i],然后将初始权值从大到小进行排序,每人就有了一个序号Di
按照这个序号对10取模的值将这些人分为10类.(也就是说定义每个人的类别序号C[i]的值为((D[i]-1) mod 10 +1)显然类别序号的取值为1-10。第i类的人将会额外得到E[i]的权值。
你需要做的就是求出加上额外权值以后,最终的权值最大的k个人,并输出他们的编号。在排序中,如果两人的W[i]相同,编号小的优先。

输入
第一行输入用空格隔开的两个整数,分别是n和k。
第二行给出了10个正整数,分别是E[1]到E[10] 。
第三行给出了n个正整数,第i个数表示编号为i的人的权值W[i] 。
题目保证:1≤k≤n≤32768,1≤W[i],E[i]≤1000。

输出
只需输出一行用空格隔开的k个整数,分别表示最终W[i]从高到低k个人的编号。

样例输入

10 10
1 2 3 4 5 6 7 8 9 10
2 4 6 8 10 12 14 16 18 20

样例输出

10 9 8 7 6 5 4 3 2 1

分析:模拟题,按照题意,用结构体存储每个人的信息,先对n个人进行编号,然后按照w初值排序,加上每个人对应的权值。最后再按照题意排序。输出k个人即可。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <vector>

using namespace std;
struct people{
    int id,w;
}p[50000];
int e[15];
int cmp(const people &a,const people &b){
    if(a.w!=b.w)return a.w>b.w;
    else return a.id<b.id;
}
int main()
{
    int n,k;
    while(~scanf("%d %d",&n,&k)){
        for(int i=1;i<=10;i++)cin>>e[i];
        for(int i=1;i<=n;i++)cin>>p[i].w,p[i].id=i;
        sort(p+1,p+1+n,cmp);
        for(int i=1;i<=n;i++)p[i].w+=e[(i-1)%10+1];//,cout<<p[i].w<<endl;
        sort(p+1,p+1+n,cmp);
        for(int i=1;i<=k;i++)cout<<p[i].id<<" ";
        cout<<endl;
    }
    return 0;
}

G:万圣节

题目描述
一年一度的万圣节马上就要到了,弓箭手小明和剑士小刚约好一起去猎杀幽灵,一共有n个幽灵,第i只幽灵会掉落ai件弓箭手装备,bi个剑士装备。
小明的收获总和是他猎杀的幽灵的ai值之和。小刚的收获总和是他猎杀的幽灵的bi值之和。小明小刚轮流行动,小明先手。两人都能保证一击必杀。
小明和小刚的目的尽可能让自己收获比对方高。你需要求出两人都使用最优策略的情况下,输出他们的收获差。我们保证所有幽灵掉落装备总价值不同。

输入
第一行输入一个正整数n表示幽灵个数
第二行输入n个数a1,a2,a3…an
第三行输入n个数b1,b2,b3…bn。
题目保证1≤n≤105, -109≤ai,bi≤109。

输出
两人都使用最优策略的情况下,他们的收获的差值(取绝对值)

样例输入

3
8 7 6
5 4 2

样例输出

10

提示
小明选择1号和3号幽灵,得到了14件装备,小刚只能选择2号幽灵得到4件装备,差值为10.

分析:贪心思维题。贪心策略:为了使自己的收获最大,小明和小刚在选择让自己收获最大的同时,还要让对方收获低。所以,选择ai+bi更大的幽灵优先猎杀。
简单证明:令1<=k1,k2<=n
设v1=a[k1]+b[k1];v2=a[k2]+b[k2];
对于小明来说,先击杀v1,与小刚的差值为a[k1]-b[k2];
先击杀v2,与小刚的差值为a[k2]-b[k1];
两种差值比较:v1-v2=(a[k1]-b[k2])-(a[k2]-b[k1])=(a[k1]+b[k1])-(a[k2]+b[k2])=v1-v2;
所以v1>v2时,选择v1最优,反之选择v2。
具体实现见代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;
struct node{
    int a,b,sum;
    bool operator <(const node &a) const{
        return a.sum<sum;
    }
}k[100005];
int main()
{
    int n;
    while(~scanf("%d",&n)){
        for(int i=0;i<n;i++)cin>>k[i].a;
        for(int i=0;i<n;i++)cin>>k[i].b;
        for(int i=0;i<n;i++)k[i].sum=k[i].a+k[i].b;
        sort(k,k+n);
        int ans1=0,ans2=0;
        for(int i=0;i<n;i++){
            if(i%2==0)ans1+=k[i].a;
            else ans2+=k[i].b;
        }
        cout<<abs(ans1-ans2)<<endl;
    }
    return 0;
}

H:最少颜色

题目描述
给定无向连通图G和m种不同的颜色,用这些颜色为图G的各顶点着色,每个顶点着一种颜色。
如果有一种着色法使G中每条边的2个顶点着不同颜色,则称这个图是m可着色的。
求解目标颜色出现最少次数的图的m着色问题。
输入一张地图(n个顶点),m种颜色(1-m),输入颜色k(1-m),要求使用m种颜色对地图进行着色,并且颜色k出现的次数最少。

输入
第1行n个顶点(编号1-n),e条边,m种颜色(编号1-m),颜色k
接下来e行,表示两个顶点之间存在的边
题目保证:1<=n<=50,n-1<=e<=500 ,1<=k<=m<=10,不存在自环与重边 。

输出
输出k出现的最少次数。
如果对于给定条件,图的m着色问题无解则输出-1

样例输入

4 3 3 1
2 4
1 2
1 3

样例输出

0

分析:dfs搜索题。先对图进行一次dfs着色,计算出这个图着色所需的最少颜色种类c,如果c<m,说明不用m种颜色就可以完成对图的着色,直接输出0;如果c>m,说明要用比给定的m种颜色还要多才可以对图着色,直接输出-1;如果c=m,还需要进行一次dfs计算颜色k出现的最少次数。在dfs最少颜色k的时候,如果大于等于之前搜索过的颜色k出现的次数,直接return。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <vector>
#include <set>

using namespace std;
int mp[55][55];
int color[55];
int col[55];
int n,e,m,k,flag,ans,cnt;
bool judge(int x,int c){//判断x是否可以染成C
    for(int i=1;i<=n;i++){
        if(mp[x][i]&&color[i]==c)return false;
    }
    return true;
}
void dfs(int id,int num){//到id为止用了num种颜色
    if(ans>num)return ;
    if(flag)return ;
    if(id>n){flag=1;return;}
    for(int i=1;i<=num;i++){
        if(judge(id,i)){
            color[id]=i;
            dfs(id+1,num);
        }
    }
    if(!flag){
        ans++;
        color[id]=ans;
        dfs(id+1,num+1);
    }
}
void dfs1(int id,int num){//判断m种颜色中k颜色使用情况最少
    if(col[k]>=cnt)return ;//如果该种颜色使用情况大于等于第一次dfs得到的结果,直接return
    if(id>n){
        cnt=col[k];
        return ;
    }
    for(int i=1;i<=num;i++){
        if(judge(id,i)){
            color[id]=i;
            col[i]++;
            dfs1(id+1,num);
            color[id]=0;
            col[i]--;
        }
    }
}
int main()
{
    cin>>n>>e>>m>>k;
    memset(color,0,sizeof(color));
    memset(col,0,sizeof(col));
    for(int i=0;i<e;i++){
        int u,v;
        cin>>u>>v;
        mp[u][v]=mp[v][u]=1;
    }
    flag=0,ans=0;
    dfs(1,0);
    if(ans>m)cout<<-1<<endl;
    else if(ans<m)cout<<0<<endl;
    else{
        for(int i=1;i<=n;i++)col[color[i]]++;
        cnt=col[k];
        memset(col,0,sizeof(col));
        memset(color,0,sizeof(color));
        dfs1(1,m);
        cout<<cnt<<endl;
    }
    return 0;
}

J:解密

题目描述
湖南中医药大学有含浦、东塘 2 个校区,学校办学历史悠久,前身为 1934 年的湖南国医专科学校,1953年创办湖南中医进修学校,1960 年创建普通高等本科院校——湖南中医学院,1979 年成为全国首批取得 中医类研究生学历教育资格的院校,1990 年原湖南科技大学成建制并入湖南中医学院,2002 年与湖南省中医药研究院合并,2006 年经教育部批准更名为湖南中医药大学,2012 年进入湖南省一本招生序列。
目前,学校与湖南省中医药研究院实行校院合一的管理体制。学校学科门类齐全、中医药特色鲜明。学校设有 18 个学院、24 个本科专业,涵盖医、理、工、管、文等 5 大学科门类。中医诊断学在本学科研究领域居国内领先水平。
小 F 居住在含浦校区,他想和东塘校区的同学小 L 聊天,为了保证沟通安全,他发明了一种奇特的加密方式,这种加密方式是这样的:对于一个 01 串,小 F 会构造另一个 01 串,使得原串是在新串中没有出现过得最短的串。
现在小 F 已经加密好了一个串,但他发现他的加密方式有些 bug,导致没出现过的最短的串不止一个,他感到非常懊恼,现在他希望计算出没出现过的最短的串的长度。

输入
单组数据。
一行一个 01 串,字符串串长小于等于 105。

输出
一行一个正整数,表示没有出现过的最短串的长度。

样例输入

100010110011101

样例输出

4

分析:题目给定一个01串,问不在该串中出现的最短子串的长度。所以我们从枚举从1到给定的字符串长度,每次判断这个长度中的01串是不是全部都出现了,如果没有全部出现。即可输出答案。那么怎么判断是否该长度的子串是否全部出现了呢?举个例子,长度为2的01串最多有四个(00,01,10,11),长度为3的01串最多有八个(000,001,010,011,100,101,110,111)。长度为n的01串最多有2^n个。(详细见代码)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <vector>
#include <set>

using namespace std;
string s;
bool check(int len){
    set<string>num;
    for(int i=0;i<s.size()-len+1;i++){
        string t=s.substr(i,len);
        num.insert(t);
        if(num.size()==pow(2,len))return false;
    }
    return true;
}
int main()
{
    cin>>s;
    for(int i=1;i<s.size();i++){
        if(check(i)){
            cout<<i<<endl;
            break;
        }
    }
    return 0;
}

K:TC的门牌号

题目描述
众所周知,TC住在比特楼,由于他不想被人打扰,于是对楼层的门牌号施加了x魔法。
现在,比特楼的门牌号排列规则如下: 门牌号[1,2]位于第一层, 门牌号[3,x+2]位于第二层, 门牌号[x+3,2x+2]位于第三层, 依次类推…
现在,TC会改变x和自己的门牌编号num,需要你来找出TC的楼层。

输入
第一行一个整数T(1<=T<=1000)表示测试数据组数,接下来T组数据。
每组数据包含两个整数num,x(1<=num,x<=10000),表示TC的门牌号和x魔法。

输出
对于每组数据,输出一个整数表示TC所在楼层

样例输入

2
5 3
10 3

样例输出

2
4

分析:据题意,除了第一层只有门牌号1,2,其它每一层楼有x个门牌号。如果在1,2门牌号,直接输出在第一层,其他情况下只需要根据一层楼有几个门牌号来判断在第几层。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <vector>
#include <set>

using namespace std;

int main()
{
    int t;
    cin>>t;
    while(t--){
        int num,x;
        cin>>num>>x;
        if(num==1||num==2){//门牌号1,2在第一层
            cout<<1<<endl;
            continue;
        }
        num-=2;//减掉门牌号为1,2的
        int ans=num/x;
        if(num%x!=0)ans++;
        cout<<ans+1<<endl;
    }
    return 0;
}

未完待续……

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

a碟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值