HNUCM2020年春季ACM集训队选拔赛(3)题解

Odessa ·
更新时间:2024-11-14
· 888 次阅读

问题 A: 手机键盘 题目描述

按照手机键盘输入字母的方式,计算所花费的时间 如:a,b,c都在“1”键上,输入a只需要按一次,输入c需要连续按三次。
如果连续两个字符不在同一个按键上,则可直接按,如:ad需要按两下,kz需要按6下。
如果连续两字符在同一个按键上,则两个按键之间需要等一段时间,如ac,在按了a之后,需要等一会儿才能按c。
现在假设每按一次需要花费一个时间段,等待时间需要花费两个时间段。
现在给出一串字符,需要计算出它所需要花费的时间。

输入

一个长度不大于100的字符串,其中只有手机按键上有的小写字母。

输出

输入可能包括多组数据,对于每组数据,输出按出Input所给字符串所需要的时间。

样例输入

bob
www

样例输出

7
7

思路

在这里插入图片描述
这是一个拼音九键的图我们知道了每一个字符需要按的次数(每按一次需要1秒钟)
如果一个字符和它前一个字符所在的按键相等它就需要等待2秒
用一个数组a保存需要按的次数a[i]表示数字i需要按的次数
用一个数组b保存所在的按键的编号b[i]表示数组i所在按键的编号…
遍历一遍得到答案

#include using namespace std; char s[105]; int main() { int a[27]={0,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,4,1,2,3,1,2,3,4}; int b[27]={0,1,1,1,2,2,2,3,3,3,4,4,4,5,5,5,6,6,6,6,7,7,7,8,8,8,8}; while(~scanf("%s",s+1)){ int ls=strlen(s+1),ans=a[s[1]-'a'+1]; for(int i=2;i<=ls;++i){ if(b[s[i]-'a'+1]==b[s[i-1]-'a'+1]){ ans+=2; } ans+=a[s[i]-'a'+1]; } printf("%d\n",ans); } } 问题 B: 买房 题目描述

某程序员开始工作,年薪N万,他希望在中关村公馆买一套60平米的房子,现在价格是200万。
假设房子价格以每年百分之K增长,并且该程序员未来年薪不变,且不吃不喝,不用交税。
每年所得N万全都积攒起来,问第几年能够买下这套房子(第一年房价200万,收入N万)。

输入

有多行,每行两个整数N(10<=N<=50), K(1<=K<=20)。

输出

针对每组数据,如果在第21年或者之前就能买下这套房子,则输出一个整数M,表示最早需要在第M年能买下,否则输出Impossible,输出需要换行。

样例输入

50 10
40 10
40 8

样例输出

8
Impossible
10

思路

每一年计算出房子的价格和这个人所挣的钱进行比较
如果在21年前工资大于等于房子价格说明能购买,反之不能

#include using namespace std; char s[105]; int main() { int n,k; while(~scanf("%d %d",&n,&k)){ int ans=0; double ad=0.01*k,a=200,b=n; for(int i=1;i<=21;++i,b+=n,a+=a*ad){ if(a<=b){ ans=i; break; } } if(ans){ printf("%d\n",ans); }else{ printf("Impossible\n"); } } } 问题 C: 与7相关的数 题目描述

一个正整数,如果它能被7整除,或者它的十进制表示法中某个位数上的数字为7, 则称其为与7相关的数。
现求所有小于等于n(n<100)的与7无关的正整数的平方和。

输入

案例可能有多组。对于每个测试案例输入为一行,正整数n,(n<100)。

输出

对于每个测试案例输出一行,输出小于等于n的与7无关的正整数的平方和。

样例输入

21

样例输出

2336

思路

按题目要求计算就行

#include using namespace std; bool ju(int x){ if(x%7==0){ return false; } while(x){ if(x%10==7){ return false; } x/=10; } return true; } int main() { int n; while(~scanf("%d",&n)){ int ans=0; for(int i=1;i<=n;++i){ if(ju(i)){ ans+=i*i; } } printf("%d\n",ans); } } 问题 D: 采药 题目描述

辰辰是个很有潜能、天资聪颖的孩子,他的梦想是称为世界上最伟大的医师。
为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。
医师把他带到个到处都是草药的山洞里对他说: “孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。
我会给你一段时间,在这段时间里,你可以采到一些草药。
如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”
如果你是辰辰,你能完成这个任务吗?

输入

输入的第一行有两个整数T(1 <= T <= 1000)和M(1 <= M <= 100),T代表总共能够用来采药的时间,M代表山洞里的草药的数目。
接下来的M行每行包括两个在1到100之间(包括1和100)的的整数,分别表示采摘某株草药的时间和这株草药的价值。

输出

可能有多组测试数据,对于每组数据,输出只包括一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。

样例输入

70 3
71 100
69 1
1 2

样例输出

3

思路

裸的01背包问题
此处是肖鹏学长的01背包博客

#include using namespace std; int a,b,dp[1005]; int main() { int t,m; while(~scanf("%d %d",&t,&m)){ memset(dp,0,sizeof(dp)); for(int i=1;i=a;--j){ dp[j]=max(dp[j],dp[j-a]+b); } } printf("%d\n",dp[t]); } } 问题 E: 鸡兔共笼 题目描述

一个笼子里面关了鸡和兔子(鸡有2只脚,兔子有4只脚,没有例外)。
已经知道了笼子里面脚的总数a,问笼子里面至少有多少只动物,至多有多少只动物。

输入

每组测试数据占1行,每行一个正整数a (a < 32768)。

输出

输出包含n行,每行对应一个输入,包含两个正整数,第一个是最少的动物数,第二个是最多的动物数,两个正整数用一个空格分开。
如果没有满足要求的答案,则输出两个0。

样例输入

2
3
20

样例输出

0 0
5 10

思路

如果脚的个数是奇数说明没有答案
否则存在答案
最少的脚的个数大于等于4当然是优先算兔子,如果最后还剩下有就用鸡补上
最多的脚数的情况就是都是鸡
也就是说有三种情况
1.脚是奇数–没有答案
2.脚数是偶数但不能被四整除–答案就是(n/4+1,n/2)
3.脚数能被四整除–答案就是(n/4,n/2)

#include using namespace std; int main() { int n,m; while(~scanf("%d",&n)){ for(int i=1;i<=n;++i){ scanf("%d",&m); if(m&1){ printf("0 0\n"); }else if(m%4){ printf("%d %d\n",m/4+1,m/2); }else{ printf("%d %d\n",m/4,m/2); } } } } 问题 F: 为了虫群 题目描述

小 Y 率领的人类的部队已经踏上了异虫的基地——查尔星。 作为异虫的首脑,你决定派出爆虫来迎击人类的部队。

爆虫身上长有充满酸液的囊。当它接近敌人时,会触发体内的不稳定化学物质进行反应,将自身引爆,向外泼洒强酸。自爆会摧毁爆虫,但同时也会对半径 R 之内(包括 距离为 R 的点 )的敌人造成大量伤害。

你观察到,人类有 n 名陆战队员,站成一条直线。 每个陆战队员的坐标是 xi。你有 k 个爆虫。爆虫的酸液会对陆战队员造成巨大伤害,将其瞬间消灭。 你可以把每只爆虫安排在直线上的任意位置,然后引爆,从而消灭距离该爆虫不超过 R 的所有陆战队员。

为了消灭所有 n 个陆战队员,你需要计算,爆虫的爆炸半径 R 至少要多少。

输入

输入共两行。第一行是用空格隔开的两个整数 n 和 k。

第二行是用空格隔开的 n 个实数,表示每个陆战队员的坐标 xi。所有坐标按升序给出。(可能存在某个位置有多名队员)
1≤k<n≤100,000

-107≤xi≤107

输出

输出共一行。爆虫的最小爆炸半径 R。保留 6 位小数。

样例输入

5 2
-10.0 -6.0 10 11 15

样例输出

2.500000

思路

因为爆虫的半径对满足单调性
也就是说
对于爆虫的半径a<b 如果b可以那么a也可以,如果a不可以,那么b也不可以
可以二分答案不断缩小可能区间,缩小到一定精度就可以跳出了

#include using namespace std; const int maxn=1e5+5; double a[maxn]; int n,k; bool ju(double mid){//判断半径为mid可不可以满足 double len=0; int num=1; for(int i=2;i<=n;++i){//循环判断mid半径下最少需要的虫子数 if(len+a[i]-a[i-1]k){//需要的虫子大于k说明不满足 return false; } return true; } int main() { while(~scanf("%d %d",&n,&k)){ for(int i=1;i0.00000001){ double mid=(l+r)/2; if(ju(mid*2)){ ans=mid; r=mid; }else{ l=mid; } } printf("%lf\n",ans); } } 问题 G: 找宝箱 题目描述

作为一个强迫症患者,小明在走游戏里的迷宫时一定要把所有的宝箱收集齐才肯罢休。

现在给你一个 N×M 的迷宫,里面有障碍、空地和宝箱,小明在某个起始点,每一步小明可以往上、下、左、右走,当然前提是没有走出迷宫并且走到的点不是障碍。如果小明走到了某个为宝箱的点,那么这个宝箱就被他收集到了,然后此处变为空地。

现在你需要计算小明最少需要走多少步才能收集齐所有的宝箱。

输入

输入共有两行。

第一行一个两个正整数 N 和 M,表示迷宫大小。

接下来 N 行,每行 M 个整数,第 i+1 行的第 j 个整数表示迷宫第 i 行第 j 列的情况, 0 表示空地,-1 表示障碍,1 表示宝箱,2 表示小明的起始点。保证 2 只有一个。

1≤N,M≤100,宝箱个数不超过5个。

输出

一行一个整数,表示小明最少的步数。如果无法收集齐所有宝箱,输出-1。

样例输入

3 5
1 -1 1 -1 2
0 -1 0 -1 0
0 0 0 0 0

样例输出

12

思路

因为宝箱的个数不多,可以用bfs处理出宝箱和起点两两之间的最短距离
再用全排列函数(next_permutation)或者dfs列出所有宝箱的访问顺序,计算步数取最小值

#include using namespace std; const int inf=0x3f3f3f3f; int w[105][105],vis[105][105],n,m,cnt; int dir[4][2]={1,0,-1,0,0,1,0,-1}; struct node{ int i,j,step; }; node chest[6]; int len[6][6],p[6]; void bfs(int st,int step){ memset(vis,0,sizeof(vis)); queue pq; node now=chest[st],ne; now.step=0,vis[now.i][now.j]=1; pq.push(now); while(!pq.empty()){ now=pq.front(),pq.pop(); if(w[now.i][now.j]==1||w[now.i][now.j]==2){//走到宝箱或者起点 for(int i=0;i<=cnt;++i){ if(chest[i].i==now.i&&chest[i].j==now.j){ len[st][i]=now.step; break; } } } for(int i=0;i=1&&ne.i=1&&ne.j<=m&&vis[ne.i][ne.j]==0&&w[ne.i][ne.j]!=-1){//没有出界,没有被走过,可以走 vis[ne.i][ne.j]=1; ne.step=now.step+1; pq.push(ne); } } } } int main(){ while(~scanf("%d %d",&n,&m)){ cnt=0; memset(len,inf,sizeof(len)); for(int i=1;i<=n;++i){ for(int j=1;j<=m;++j){ scanf("%d",&w[i][j]); if(w[i][j]==1){ chest[++cnt].i=i; chest[cnt].j=j; } if(w[i][j]==2) chest[0].i=i,chest[0].j=j; } } for(int i=0;i<=cnt;++i){//处理宝箱和起点两两间的距离 bfs(i,0); } for(int i=0;i<=cnt;++i) p[i]=i; int ans=inf; do{ int num=0; for(int i=1;i=inf) ans=-1; printf("%d\n",ans); } } 问题 H: gcd 区间 题目描述

给定一行 n 个正整数 a[1]…a[n]。

m 次询问,每次询问给定一个区间[L,R],输出 a[L]…a[R]的最大公因数。

输入

第一行两个整数 n,m。

第二行 n 个整数表示 a[1]…a[n]。

以下 m 行,每行 2 个整数表示询问区间的左右端点。

保证输入数据合法。

1 <= n <= 1000,1 <= m <= 1,000,000 0 < 数字大小 <= 1,000,000,000

输出

共 m 行,每行表示一个询问的答案。

样例输入

5 3
4 12 3 6 7
1 3
2 3
5 5

样例输出

1
3
7

思路

n的范围不大,可以先预处理出所有情况,最后直接输出就行,当然线段树和树状数组也可

#include #define ll long long using namespace std; ll a[1005],ans[1005][1005]; ll gcd(ll a,ll b){ if(b==0) return a; return gcd(b,a%b); } int main(){ int n,m; while(~scanf("%d %d",&n,&m)){ for(int i=1;i<=n;++i){ scanf("%lld",&a[i]); } for(int i=1;i<=n;++i){ ans[i][i]=a[i]; for(int j=i+1;j<=n;++j){ ans[i][j]=gcd(a[j],ans[i][j-1]); } } int l,r; for(int i=1;i<=m;++i){ scanf("%d %d",&l,&r); printf("%lld\n",ans[l][r]); } } }
作者:名字在哪啊



acm

需要 登录 后方可回复, 如果你还没有账号请 注册新账号
相关文章