A-幸福小组
题目描述
俗话说:男女搭配,干活不累。还据说,如果在做一件事情时男生和女生人数相等,学习和工作效率可以达到最佳【瞎说的】。
为了更好地学习计算机专业知识,某个班分为n个学习小组,如果某一个组中男生和女生人数相等,这样的小组称为“幸福小组”。
现在分别给出这n个小组中男生和女生人数,请你编写一个程序统计一共有多少个“幸福小组”。
输入
单组输入。第1行输入一个正整数n,表示学习小组数量。(n<=100)
接下来n行每行包含两个正整数,分别表示每一个学习小组中男生人数和女生人数,两个数字之间用空格隔开。
输出
“幸福小组”的个数。
样例输入
4
1 5
5 1
3 3
4 4
样例输出
2
分析:签到题,一个循环就可以了!
#include <iostream>
#include <cstdio>
using namespace std;
int main(){
int n,co=0,m,f;
scanf("%d",&n);
while(n--){
scanf("%d%d",&m,&f);
if(m==f){
co++;
}
}
printf("%d\n",co);
return 0;
}
B-菜鸡驿站
题目描述
HNUCM的TC同学在学校开设了一个收发快递的“菜鸡驿站”,HNUCM一共有n栋宿舍楼,TC需要逐一把快递送到每一栋宿舍楼。
因为刚过完双十一,快递实在是太多太多了,TC每一次只能送一栋宿舍楼,然后再返回驿站取快递来送到下一栋宿舍楼。
现在给出驿站到每一栋宿舍楼的单程时间(单位:分钟,假设来回所需的时间相等),请问TC送完所有快递并返回驿站一共需要多少分钟的时间?
输入
单组输入,每组第1行输入一个正整数n表示宿舍楼的数量(n<=50)。
第2行输入n个正整数分别表示驿站到每一栋宿舍楼所需时间T(T<=10^9)(单位:分钟)。
输出
送完所有快递并返回到驿站所需的总时间(分钟)。
样例输入
5
3 4 5 6 7
样例输出
50
分析:很简单的题,不过要注意数据的大小,时间要开到long long。注意小细节嗷!
#include <iostream>
#include <cstdio>
using namespace std;
#define ll long long
int main(){
int n,m;
ll T=0;
scanf("%d",&n);
while(n--){
scanf("%d",&m);
T+=m*2;
}
printf("%lld\n",T);
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
样例输出
-1
1
11
提示
TC说火柴要省着用
分析:偏思维的题目,体现了贪心的思想,想清楚就好了。当a<2时不能拼数。当a>2时,若a为偶数,组成一个有 a/2 位全为1的正整数,若a为奇数,则第一位为7,后面 (a-3)/2 位均为1的正整数,这样拼数得到的数最大。不要用long long去存,数字太大了,直接根据位数输出数字即可。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
#define ll long long
int main()
{
int n,x;
scanf("%d",&n);
while(n--){
scanf("%d",&x);
if(x<2){//对x<2的特判
printf("-1\n");
}
else{
if(x%2!=0){
printf("7");//奇数,先输出7
int k=(x-3)/2;
while(k--){
printf("1");
}
printf("\n");
}
else{
int k=x/2;
while(k--){
printf("1");//偶数直接输出1
}
printf("\n");
}
}
}
return 0;
}
PS:我比赛是用dfs,居然过了,可能测试点数据很小,也可以过吧。嘿嘿,但是最好不要这样写!我只是刚好学习了dfs,恰好想到了,就用了。就当练习一下dfs吧!
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
#define ll long long
ll maxn=-1;
int a[10]={6,2,5,5,4,5,6,3,7,6};
void dfs(int n,ll sum){
if(n<0) return ;
if(n==0){
if(maxn<sum) maxn=sum;
return;
}
for(int i=0;i<9;i++){
dfs(n-a[i],sum*10+i);
}
}
int main(){
int n,T;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
dfs(n,0);
printf("%lld\n",maxn);
maxn=-1;
}
return 0;
}
D-n盏灯
题目描述
有n盏灯,序号为1到n。在初始状态下,所有的开关都是关的。
有n个开关控制这n盏灯,第i个开关控制第i盏灯。
现在从1开始计数,每遇到一个素数就把该素数对应的倍数序号的开关朝相反的方向拨一下,一直到最后一个小于等于n的素数为止。请统计最后有多少盏灯是开的?
输入
多组输入,每组输入一个n(2<=n<=10^6)。
输出
每组数据,输出开的灯的盏数
样例输入
10
样例输出
7
分析:模拟题,不过要注意卡时间,很容易时间超限。在判断完素数后,模拟开关序号的j从该素数开始,并且每次都累加该素数。用数组模拟所有灯,初始化数组为0表示灯是关的,每次需要拨动开关时,取反。最后统计开着的灯的数目。(错了7次,哭死!!!)
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
#define ll long long
const int maxn=1e6+5;
int light[maxn];
bool prime(int n){//六素数法判断素数
if(n==1) return false;
else if(n==2||n==3||n==5) return true;
else if(n%2==0||n%3==0) return false;
int k=sqrt(n);
for(int i=5;i<=k;i+=6){
if(n%i==0||n%(i+2)==0) return false;
}
return true;
}
int main()
{
int n,x;
while(~scanf("%d",&n)){
int co=0;
memset(light,0,sizeof(light));//数组记得清0
for(int i=1;i<=n;i++){
if(!prime(i)) continue;//如果不是素数直接跳出循环
for(int j=i;j<=n;j+=i){//直接累加该素数,卡时间
light[j]=!light[j];
}
}
for(int i=1;i<=n;i++){
if(light[i]!=0) co++;
}
printf("%d\n",co);
}
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
分析:也是一道模拟题,对时间进行模拟就好了。注意一下秒钟、分钟和小时之间的进位就好了。但是比赛时我没有写出来。可惜可惜,继续加油!
越想越气,好可惜啊!!!果然比赛心态还是要放好!!!
#include <iostream>
#include <cstdio>
using namespace std;
#define ll long long
int check(int n){
if(0<n&&n<=10) return 1;
else if(n==0) return 2;
int co=0;
while(n){
if(n%10==0) co++;
n/=10;
}
return co;
}
int main(){
int h1,h2,m1,m2,s1,s2;
int t=0;
scanf("%d:%d:%d",&h1,&m1,&s1);
scanf("%d:%d:%d",&h2,&m2,&s2);
while(!(h1==h2&&m1==m2&&s1==s2)){
t+=check(h1);
t+=check(m1);
t+=check(s1);
s1+=1;
if(s1==60){
s1=0;
m1+=1;
}
if(m1==60){
m1=0;
h1+=1;
}
}
t+=check(h2);
t+=check(m2);
t+=check(s2);
printf("%d\n",t);
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
分析:用结构体来写比较好,也算比较简单的题,考察结构体和排序。注意要读懂题目,一开始每个人就有一个序号,这个序号和后面获得额外权值的序号不同。样例真的太特殊了!!!很有迷惑性!
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
#define ll long long
const int maxn=32770;
struct men{
int no;
int num;
};
struct men m[maxn];
int e[11],w[maxn];
bool cmp(men a,men b){
return a.num>b.num;
}
bool cmp1(men a,men b){
if(a.num!=b.num) return a.num>b.num;
else return a.no<b.no;
}
int main(){
int n,k;
scanf("%d%d",&n,&k);
for(int i=1;i<=10;i++){
scanf("%d",&e[i]);
}
for(int i=1;i<=n;i++){
scanf("%d",&m[i].num);
m[i].no=i;//一开始的序号
}
sort(m+1,m+n+1,cmp);
for(int i=1;i<=n;i++){
m[i].num+=e[(i-1)%10+1];//对于取额外权值的序号,就是根据初始权值排序后的新序号
}
sort(m+1,m+n+1,cmp1);//根据权值和初始序号排序
for(int i=1;i<=k;i++){
if(i==1) printf("%d",m[i].no);
else printf(" %d",m[i].no);
}
printf("\n");
return 0;
}
G- 万圣节
题目描述
一年一度的万圣节马上就要到了,弓箭手小明和剑士小刚约好一起去猎杀幽灵,一共有n个幽灵,第i只幽灵会掉落ai件弓箭手装备,bi个剑士装备。
小明的收获总和是他猎杀的幽灵的ai值之和。小刚的收获总和是他猎杀的幽灵的bi值之和。小明小刚轮流行动,小明先手。两人都能保证一击必杀。
小明和小刚的目的尽可能让自己收获比对方高。你需要求出两人都使用最优策略的情况下,输出他们的收获差。我们保证所有幽灵掉落装备总价值不同。
输入
第一行输入一个正整数n表示幽灵个数
第二行输入n个数a1,a2,a3…an
第三行输入n个数b1,b2,b3…bn。
题目保证1≤n≤10^5, -10^9≤ai ,bi≤10^9。
输出
两人都使用最优策略的情况下,他们的收获的差值(取绝对值)
样例输入
3
8 7 6
5 4 2
样例输出
10
提示
小明选择1号和3号幽灵,得到了14件装备,小刚只能选择2号幽灵得到4件装备,差值为10.
分析:一道体现贪心思想的题,用结构体存ai,bi。在自定义结构体的排序就好了。具体分析:假设有两个幽灵,1和2,若小明选1,小刚就会选2,收获的差值:a1-b2、若小明选2,小刚就会选1,收获的差值:a2-b1。比较两种方式:(a1-b2)-(a2-b1)=(a1+b1)-(a2+b2),当第一个幽灵和最大,则旋盖幽灵为最优,反之亦然。相当于对两个幽灵掉落装备的所有可能之和比较,先选最大的。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
#define ll long long
const int maxn=1e5+1;
struct ghost{
ll a,b;
};
struct ghost gh[maxn];
bool cmp(ghost c,ghost d){//自定义的排序规则
return c.a+c.b>d.a+d.b;
}
int main(){
int n;
ll xm=0,xg=0;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%lld",&gh[i].a);
}
for(int i=0;i<n;i++){
scanf("%lld",&gh[i].b);
}
sort(gh,gh+n,cmp);//sort排序
for(int i=0;i<n;i++){
if(i%2==0) xm+=gh[i].a;
else xg+=gh[i].b;
}
printf("%lld\n",abs(xm-xg));//注意输出绝对值
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判断该图最少需要的颜色个数c,如果c>m,该图无解,直接输出-1、如果c<m,则输出0,涂色的时候不用颜色k。如果c=m,则继续用第二个dfs,判断颜色k最少使用多少次。(真的有点不会写,看题解也有点没看懂,最后是看了大佬的代码才懂了那么一点点,感谢 a碟
)。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
#define ll long long
const int maxn=1e5+1;
int chart[55][55],p[55],color[20];//三个数组分别记录图,结点是否涂色,颜色的使用
int n,m,k,e;
int ans=0,flag=0,sum=0;
bool check(int c,int o){
for(int i=1;i<=n;i++){
if(chart[c][i]==1&&p[i]==o){//检验该结点能否涂颜色o
return false;
}
}
return true;
}
void dfs1(int b,int num){//最少颜色的搜索
if(flag)return;
if(b>n){flag=1;return;}
for(int i=1;i<=num;i++){//每次都从已使用过的颜色开始检验,使颜色的使用最少
if(check(b,i)){
p[b]=i;
dfs1(b+1,num);
}
}
if(!flag){//如果已使用的颜色都不能涂,新加一个颜色。
ans++;//记录颜色的使用个数
p[b]=ans;
dfs1(b+1,num+1);
}
}
void dfs(int co){//当使用颜色为m时,对颜色k使用最少的搜索
if(color[k]>=sum) return;
if(co>n){
sum=color[k];
return ;
}
for(int i=1;i<=m;i++){
if(check(co,i)){
p[co]=i;
color[i]++;
dfs(co+1);
color[i]--;
p[co]=0;
}
}
}
int main(){
int a,b;
scanf("%d%d%d%d",&n,&e,&m,&k);
memset(chart,0,sizeof(chart));
memset(p,0,sizeof(p));
for(int i=1;i<=e;i++){
scanf("%d%d",&a,&b);
chart[a][b]=chart[b][a]=1;//二维数组记录图
}
dfs1(1,0);
if(ans>m) printf("-1\n");
else if(ans<m) printf("0\n");
else{
sum=n;
memset(p,0,sizeof(p));
memset(color,0,sizeof(color));
dfs(1);
printf("%d\n",sum);
}
return 0;
}
I-2048
题目描述
TC喜欢2048,甚至想出了一款游戏。
有n个网格排列在一行,在游戏开始时,一些网格已经被填充了2或4。
首先,你需要用2或4填充空的网格,若两个数相邻且相等即可将它们相加合并。
最终目标是使得最大的数字大于等于2k。
现在,给你游戏的初始状态,空的网格用0表示。请你来帮TC计算出有多少种方案,使得网格中最大的数字大于等于2k。
输入
第一行包含两个整数 n, k. (n<=2000,k<=11)
第二行包含n个整数 a1,a2,…,an(ai = 0, 2, 4).
输出
输出一个整数表示答案, 由于答案可能很大, 请对 109+7取模.
样例输入
5 4
2 0 0 4 4
样例输出
2
J-解密
题目描述
湖南中医药大学有含浦、东塘 2 个校区,学校办学历史悠久,前身为 1934 年的湖南国医专科学校,1953年创办湖南中医进修学校,1960 年创建普通高等本科院校——湖南中医学院,1979 年成为全国首批取得
中医类研究生学历教育资格的院校,1990 年原湖南科技大学成建制并入湖南中医学院,2002 年与湖南省中医药研究院合并,2006 年经教育部批准更名为湖南中医药大学,2012 年进入湖南省一本招生序列。
目前,学校与湖南省中医药研究院实行校院合一的管理体制。学校学科门类齐全、中医药特色鲜明。学校设有 18 个学院、24 个本科专业,涵盖医、理、工、管、文等 5 大学科门类。中医诊断学在本学科研究领域居国内领先水平。
小 F 居住在含浦校区,他想和东塘校区的同学小 L 聊天,为了保证沟通安全,他发明了一种奇特的加密方式,这种加密方式是这样的:对于一个 01 串,小 F 会构造另一个 01 串,使得原串是在新串中没有出现过得最短的串。
现在小 F 已经加密好了一个串,但他发现他的加密方式有些 bug,导致没出现过的最短的串不止一个,他感到非常懊恼,现在他希望计算出没出现过的最短的串的长度。
输入
单组数据。
一行一个 01 串,字符串串长小于等于 105。
输出
一行一个正整数,表示没有出现过的最短串的长度。
样例输入
100010110011101
样例输出
4
分析:一个长度为k的01串有2^k 个不同的形式,例:k=3,000、001、010、011、100、101、110、111.从1开始在加密串中截取长度为i的字符串,判断其不同长度为i的子串个数是否符合要求(又是没想到的题,又是看了题解也没怎么想到的题,还是看了大佬的题解才懂了那么一点点的题。感谢a碟)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<set>
using namespace std;
string a;
bool check(int k){
set<string> s;//set自动去重
for(int i=0;i<=a.size()-k;i++){
s.insert(a.substr(i,k));
}
if(s.size()<pow(2,k)) return true;//检验子串的个数
return false;
}
int main(){
cin>>a;
if(a.size()==1){
printf("1\n");
}
else{
for(int i=1;i<=a.size();i++){
if(check(i)){
printf("%d\n",i);
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个门牌号。当num<=2,就输出1,num>2,就输出((num-3)/x)+2.比赛的时候,我是用循环写的,利用给出的公式,对第一层特判。
正确想法的代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
#define ll long long
int main(){
int num,x,T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&num,&x);
if(num<=2){
printf("1\n");
}
else{
printf("%d\n",(num-3)/x+2);
}
}
return 0;
}
我比赛时的代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
#define ll long long
int main(){
int num,x,T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&num,&x);
if(num<=2){
printf("1\n");
}
else{
for(int i=1;;i++){
if((i-1)*x+3<=num&&num<=i*x+2){
printf("%d\n",i+1);
break;
}
}
}
}
return 0;
}
L-粗心的小明
题目描述
粗心的小明总是犯错,所以他每次出错时总是希望撤销。
现在小明手中有一个栈,支持三种基本操作:入栈、出栈和查询栈顶元素,同时还支持两个高级的操作:撤销(撤销只针对入栈和出栈操作)和重做(当撤销过多时可重做,即返回一次撤销)。
注意:
1.每次入栈和出栈都会使当前状态为最新状态,并且之前撤销的操作将无法重做。
2.如果当前状态已经是最原始的状态,则撤销操作无效。
3.如果当前状态已经是最新状态,则重做操作无效。
输入
第一行一个整数n (n≤106)表示操作数
接下来n行,每行代表一个操作:
1 a 表示a(0≤a≤109)入栈
2 表示出栈
3 表示查询栈顶元素
4 表示撤销
5 表示重做
输出
对于每次查询,输出栈顶元素
样例输入
10
1 1
4
5
1 2
4
3
5
3
2
3
样例输出
1
2
1
M-小明的魔方
题目描述
数学家已经证明,任意一组操作重复有限次后都会使魔方回到原来的状态。
小明认为:既然能使所有块回到原来的状态,那么一定能使所有块回到原来的位置,并且这个操作次数一定更小。
比如下面这种情况,所有块都在原来的位置上,只不过其中两个棱块反了。
现给定一组操作,要求至少重复多少次这组操作可以让魔方所有的块都回到原来的位置。
cjm大佬特意为这次的选手写了一个魔方程序,魔方链接如下:
魔方链接
输入
输入包含一个字符串str(1≤len(str)≤2×105),这个字符串只包含U,u,D,d,L,l,R,r,F,f,B,b这12个字符,
其中U,D,L,R,F,B分别表示上面,下面,左面,右面,前面和后面顺时针旋转(当你正对此面时,此面顺时针旋转),相应地u,d,l,r,f,b表示逆时针旋转
输出
输出一个整数,表示重复次数k(k≥1)。
样例输入
LBUR
样例输出
8
提示
样例解释:
第1次操作:
第2次操作:
第3次操作:
第4次操作:
第5次操作:
第6次操作:
第7次操作:
第8次操作:
经过8组操作之后,所有块都在对应的位置上,但是其中有6个角块方向不对。
还有几道题超出了我的能力范围,暂时是不能写出来,先把题目列在这里,慢慢补题!!!未完待续……
总结
这算是我第一次比赛,虽然只是校级的,而且没排的上名次,但收获还是很大。比赛心态真的要放好,不急不躁,面对有思路但迟迟不过的题,要想放一放,到后面再回去补,不要一直死磕一道题,真的得不偿失。要时刻保持冷静,理性思考,仔细审题。注意题目的关键的词,有时候解答的方法就在题目的字眼里。
继续加油!下次要争取拿奖,下次选拔赛我也要参加。多写题,多学算法。加油加油!!!感谢那些帮助过我的人!!