牛客网 2018年全国多校算法寒假训练营练习比赛(第一场) 题解

时间:2023-01-07 00:17:02

A-大吉大利,今晚吃鸡——枪械篇

题目描述
在绝地求生(吃鸡)游戏里,不同的枪支有不同的威力,更是可以搭配不同的配件,以提升枪支的性能。
牛客网 2018年全国多校算法寒假训练营练习比赛(第一场) 题解
每一把枪都有其威力及其可装备配件种类。每一个配件有其所属种类,可以为枪支提供威力的百分比加成。每一把枪只能装备一个同类配件。给你n把枪支和m个配件,枪的威力为p,可装备的配件数量为k,为k个不同类型的配件,同种类配件只可以装备一个。配件种类用数字q表示,配件威力加成用一个小数b表示。请你挑选一把枪并为其搭配配件使其威力最大。
假设一把枪的威力是p,装配的k个配件的威力加成是bi,那么枪最后的威力w=p*(1+b1+b2+…+bk)。
备注:
对于100%的数据,
1 <= n,m,k,q <= 1000;
0 <= p <= 1000;
0 <= b <= 1。
链接:https://www.nowcoder.com/acm/contest/67/A
思路:
对配件威力加成bi贪心保留最大值。
注意多组数据和每一组数据要初始化。
代码:

#include <bits/stdc++.h>
using namespace std;
double b[1005];
int a[1005][1005];
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m)){//多组数据
for(int i=0;i<1005;i++) b[i]=0.0;//初始化
for(int i=0;i<n;++i){
scanf("%d%d",&a[i][0],&a[i][1]);
for(int j=2;j<2+a[i][1];++j){
scanf("%d",&a[i][j]);
}
}
for(int i=0;i<m;++i){
int q;
double bb;
scanf("%d%lf",&q,&bb);
b[q]=max(b[q],bb);
}
double ans=0;
for(int i=0;i<n;i++){
double maxx=1;
for(int j=2;j<2+a[i][1];j++){
maxx+=b[a[i][j]];
}
maxx*=a[i][0];
ans=max(ans,maxx);
}
printf("%.4lf\n",ans);
}
return 0;
}

B-最强的决斗者一切都是必然的!

题目描述
L一直喜欢玩游戏王这款声控印卡游戏,使用一套连锁式削血卡组便可战无不胜。每当陷入危机即将败北之际,L便会高呼“最强的决斗者一切都是必然的!”,然后发动闪光印卡技能,直接翻盘,伤害不多不少,正好足够击败对手。
发动闪光印卡技能后,L抽取一张牌,然后微微一笑。接着L以一定顺序打出若干张牌,造成的伤害正好等于对方的生命值。每一张牌都有其发动速度以及效果。如果后发动的一张牌的发动速度不小于前一张牌,则后发动的那张牌会在前一张牌后进行连锁发动,这张牌的连锁数就是连锁发动的编号。不进行连锁发动的牌,连锁数为1。同一连锁中的牌,后发动的牌先生效。
如下图,5张牌的速度分别为(1,2,2,2,3),因此它们进行连锁发动。连锁数分别为(1,2,3,4,5),因为连锁中的牌,后发动的先生效,所以,生效顺序为(5,4,3,2,1)。
牛客网 2018年全国多校算法寒假训练营练习比赛(第一场) 题解
为简化问题,我们假设发动的牌的效果有如下几种:
1. 对对方造成X点伤害
2. 对对方造成这张牌的连锁数乘X点的伤害
3. 同一连锁中的牌全部无效
4. 连锁中的前一张牌无效
现在你知道L发动牌的效果、速度和顺序,求L能对对方造成多少伤害。
链接:https://www.nowcoder.com/acm/contest/67/B
备注:
对于100%的数据,
1 <= n,x <= 1000;
1 <= s <= 3;
1 <= t <= 4。
思路:
模拟一下就好了。
代码:

#include <bits/stdc++.h>
using namespace std;
int a[1005][3];
int main()
{
int n;
while(~scanf("%d",&n)){
int s=1,e=1;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&a[i][0],&a[i][1]);
if(a[i][1]<=2) scanf("%d",&a[i][2]);
}
int ans=0;
for(int i=1;i<=n;i++)
{
if(a[i][0]>a[i+1][0] || i+1>n){
e=i;
for(int p=e;p>=s;p--)
{
if(a[p][1]==1) ans+=a[p][2];
if(a[p][1]==2) ans+=a[p][2]*(p-s+1);
if(a[p][1]==3) p=s-1;
if(a[p][1]==4) p--;
}
s=i+1;
}
}
printf("%d\n",ans);
}
return 0;
}

链接:https://www.nowcoder.com/acm/contest/67/C
来源:牛客网

C-六子冲

题目描述
六子冲是流传于中国民间的一类棋类游戏。由于这个游戏对环境的要求不高,孩子们大都是在光滑的地面或石板上画上方格,以石子或木棍、草节等为棋子,并有简单的比赛,可以锻炼脑力。
牛客网 2018年全国多校算法寒假训练营练习比赛(第一场) 题解
棋子只能停留在棋盘上的落子点,棋子只能在线上移动,棋子只能移动一步(即相邻落子点),每回合只能移动1个棋子。消灭对方棋子的方法只有一条,也很简单。那就是:二子打一子。即在棋盘上攻击方的2个棋子(2子必须相连并主动移动其中的1个)与被攻方的1个棋子皆处在一条直线上并相邻时,被攻方的这个棋子就被消灭。双方轮流走子,保护自己的棋子并消灭所有对方的棋子,直到最后胜利。
吃子例与错误吃子例如下图所示:
牛客网 2018年全国多校算法寒假训练营练习比赛(第一场) 题解牛客网 2018年全国多校算法寒假训练营练习比赛(第一场) 题解
牛客网 2018年全国多校算法寒假训练营练习比赛(第一场) 题解牛客网 2018年全国多校算法寒假训练营练习比赛(第一场) 题解
现为双方棋子赋予编号1~12。1~6号为黑方棋子,7~12号为白方棋子。其初始位置如下:
用两个整数,来代表走子方式。第一个数q代表棋子的编号,第二个数p,代表走子的方向。1<=q<=12,1<=p<=4,其中q的数字对应棋子的编号,p为1时向上走子,p为2时向下,3为向左,4为向右。给你n步走子方式,求最后棋盘的局面。
备注:
对于100%的数据,
1 <= n <= 1000;
1 <= q <= 12;
1 <= p <= 4。
题解
对着8个案例慢慢磨慢慢模拟吧。其实能消的就一种情况,一条线上,我方两个子相邻,对方一个子与我方任一子相邻,还有一个点无子,且我方主动移动。
代码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int ma[4][4]={1,0,0,0,8,0,0,0,8,0,0,0,0,0,0,0};
void init()
{
ma[0][0]=11;ma[0][1]=10;ma[0][2]=9;ma[0][3]=8;
ma[1][0]=12;ma[1][1]=0;ma[1][2]=0;ma[1][3]=7;
ma[2][0]=1;ma[2][1]=0;ma[2][2]=0;ma[2][3]=6;
ma[3][0]=2;ma[3][1]=3;ma[3][2]=4;ma[3][3]=5;
}
int judge(int x,int y)
{
if(ma[x][y]<=6&&ma[x][y]>0) return 0;
else if(ma[x][y]>6) return 1;
else return -1;
}
void solve(int x,int y)
{
if(judge(x,y)==0)//1~6
{
int j=-1,k=-1,l=0;//0的位置,7~12的位置,1~6的数量
for(int i=0;i<4;i++){//先横向
if(ma[x][i]==0) j=i;
if(judge(x,i)==1) k=i;
if(judge(x,i)==0) l++;
}
if(j!=-1&&k!=-1&&(abs(j-k)==3 || abs(j-k)==1&&(j==0&&k==1||j==3&&k==2))&&l==2){//可以吃
ma[x][k]=0;
}
j=-1,k=-1,l=0;//0的位置,7~12的位置,1~6的数量
for(int i=0;i<4;i++){
if(ma[i][y]==0) j=i;
if(judge(i,y)==1) k=i;
if(judge(i,y)==0) l++;
}
if(j!=-1&&k!=-1&&(abs(j-k)==3 || abs(j-k)==1&&(j==0&&k==1||j==3&&k==2))&&l==2){//可以吃
ma[k][y]=0;
}
}else if(judge(x,y)==1){//7~12
int j=-1,k=-1,l=0;//0的位置,1~6的位置,7~12的数量
for(int i=0;i<4;i++){//先横向
if(ma[x][i]==0) j=i;
if(judge(x,i)==0) k=i;
if(judge(x,i)==1) l++;
}
if(j!=-1&&k!=-1&&(abs(j-k)==3 || abs(j-k)==1&&(j==0&&k==1||j==3&&k==2))&&l==2){//可以吃
ma[x][k]=0;
}
j=-1,k=-1,l=0;//0的位置,1~6的位置
for(int i=0;i<4;i++){
if(ma[i][y]==0) j=i;
if(judge(i,y)==0) k=i;
if(judge(i,y)==1) l++;
}
if(j!=-1&&k!=-1&&(abs(j-k)==3 || abs(j-k)==1&&(j==0&&k==1||j==3&&k==2))&&l==2){//可以吃
ma[k][y]=0;
}
}
}
void mov(int q,int p)
{
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
if(ma[i][j]==q){
int x=i,y=j;
ma[x][y]=0;
if(p==1) ma[--x][y]=q;
if(p==2) ma[++x][y]=q;
if(p==3) ma[x][--y]=q;
if(p==4) ma[x][++y]=q;
solve(x,y);
return ;
}
}
}
}
int main()
{
int cas=1;
int n;
while(~scanf("%d",&n))
{
init();
while(n--)
{
int q,p;
scanf("%d%d",&q,&p);
mov(q,p);
}
printf("#Case %d:\n",cas++);
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
printf("%3d",ma[i][j]);
}
printf("\n");
}
}
}

F-大吉大利,今晚吃鸡——跑毒篇

题目描述
现在有一款很火的游戏playerunknown’s battlegrounds,人称“吃鸡”,在里面经常面临跑毒(从安全区外跑进安全区内)的问题,在安全区外,人们会处于中毒状态,每秒会掉a%血,人们可以通过使用道具急救包把血量升回到80%,使用急救包需要原地站着6秒。现在知道在安全区外扣血速度为a%/s,角色和安全区的距离为b米,角色跑步速度为1m/s,角色有c个急救包,请问角色是否能安全跑进安全区内。(PS:角色开始的血量为100%。如果血量降到0%,立刻判定为死亡。使用急救包时,如果刚使用完毕瞬间或者正在使用急救包的时候,血量降到0%,角色立即判定为死亡。顺带一提,这里判断时间不存在0.xxxx秒,最小时间单位为1s)。
链接:https://www.nowcoder.com/acm/contest/67/F
备注:
对于100%的数据,
1 <= T < 9;
0 < a <= 20;
0 < b <= 120;
0 <= c <= 8。
思路
一般我们按最坏情况来,先判断毒死之前能不能吃药,药够不够跑进,能不能直接跑进去。根据这几个问题,特别判断即可。
代码

#include <bits/stdc++.h>
using namespace std;
int main()
{
int T;
cin>>T;
while(T--)
{
int a,b,c;
cin>>a>>b>>c;
int now=0;//当前跑了多远
int hp=100;
int t=0;
int flag=1;
while(now<b){
if(a*6+1<=hp && a*7>=hp || hp/a<6)
{
if(b-now<=ceil(hp*1.0/a)){now=b;break;}//能直接跑进圈
if(c==0){
if(b-now>ceil(hp*1.0/a)) break;//不能吃药,毒死
else now=b;//能直接跑进圈
}
else{//打药
t+=6;
hp=80;
c--;
}
}
else{
now++;
t++;
hp-=a;
}
}
cout<<(now>=b?"YES":"NO")<<endl;
}
return 0;
}

H-方块与收纳盒

题目描述
现在有一个大小n*1的收纳盒,我们手里有无数个大小为1*1和2*1的小方块,我们需要用这些方块填满收纳盒,请问我们有多少种不同的方法填满这个收纳盒。
备注:
对于100%的数据,
0 < T < 80;
0 < n <= 80。
链接:https://www.nowcoder.com/acm/contest/67/H
思路:
斐波拉契数列……注意别超时即可……
代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL a[82];
LL f(int N)
{
if(a[N]) return a[N];
else{
a[N]=f(N-1)+f(N-2);
return a[N];
}
}
int main()
{
int T;
int N;
a[1]=1;
a[2]=2;
cin>>T;
while(T--)
{
cin>>N;
cout<<f(N)<<endl;
}
}

I-找数字个数

题目描述
lulu喜欢小于等于1000的正整数,但是如果某个数是a或b的倍数,lulu会讨厌这个数。如果某个数里包含了a和b两个数里包含的数,lulu也会讨厌。(例如a=14,b=23,如果数字中包含1、2、3、4这四个数中的任意一个数,lulu就会讨厌这个数)。现在告诉你a,b,你能说出lulu喜欢的数有多少个么。
备注:
对于100%的数据,
0 < T <= 20;
0 <= a <= 99999;
0 <= b <= 99999。
链接:https://www.nowcoder.com/acm/contest/67/I
思路:
暴力枚举即可。先用num[]数组记录包含哪些数字,再枚举每一个符合的数字。因为1000以内,不用担心超时。
代码:

#include <bits/stdc++.h>
using namespace std;
bool num[12];
int main()
{
int T;
int a,b;
cin>>T;
while(T--){
memset(num,0,sizeof(num));
cin>>a>>b;
int t=a;
while(t){
num[t%10]=1;
t/=10;
}
t=b;
while(t){
num[t%10]=1;
t/=10;
}
int res=0;
for(int i=1;i<=1000;i++){
int t=i;
int flag=0;
while(t)
{
if(num[t%10]){flag=1;break;}
t/=10;
}
if(i%a==0||i%b==0) flag=1;
if(!flag) res++;
}
cout<<res<<endl;
}
return 0;
}

G-圆圈

题目描述
圈圈圆圆圈圈,lulu小朋友最近看喜羊羊看多了,老是受刺激就画圆圈,听到小于8的数字时,还会画出十分有规律的圆圈,现在你需要根据样例观察出规律,编写程序,根据输入的数字n(n<8),输出对应的圆圈。
备注:
对于100%的数据,
0< T<9;
0<=n<8;
链接:https://www.nowcoder.com/acm/contest/67/G
思路
递归思路。通过观察,图形是上面一个,中间两个,下面一个。例如n=3,则是上面一个(n=2)的图形,中间两个(n=2)的图形,下面一个(n=2)的图形。递归之,n=2,则是上面一个(n=1)的图形,中间两个(n=1)的图形,下面一个(n=1)的图形。
注意处理尾部空格,输出即可。
代码:

#include <bits/stdc++.h>
using namespace std;
const int MAXN=1e5+100;
typedef long long LL;
char a[3000][3000];
int pow3[10];
void draw(int n,int x,int y)
{
if(n==0){
a[x][y]='O';
}
else{
int px=pow3[n-1];
draw(n-1,x,y+px);//上
draw(n-1,x+px,y);draw(n-1,x+px,y+2*px);//中
draw(n-1,x+px*2,y+px);//下
}
}
void _print(int n)
{
for(int i=0;i<pow3[n];i++){
for(int j=pow3[n];j>=0;j--){
if(a[i][j]=='O' ){//找到最右边的O,然后输出该O前面的内容
a[i][j+1]='\0';break;
}
}
puts(a[i]);
}
}
int main()
{
pow3[0]=1;
for(int i=1;i<8;i++){
pow3[i]=pow3[i-1]*3;
}
int T;
cin>>T;
int n;
while(T--){
for(int i=0;i<3000;i++)for(int j=0;j<3000;j++) a[i][j]=' ';
cin>>n;
draw(n,0,0);
_print(n);
}
return 0;
}

J-闯关的lulu

题目描述
勇者lulu某天进入了一个高度10,000,000层的闯关塔,在塔里每到一层楼,他都会获得对应数量的0 1(看情况获得),然后塔里有一个法则,当你身上某个数字达到一个特定的数量时,它们会合成为下一个数字,现在问题来了,当lulu从1层到达第n层的时候,他身上的数字是多少。
第1层 0
第2层 11
第3层 110
第4层 21
第5层 210
第6层 22
第7层 220
第8层 2211
第9层 22110
第10层 2221
第11层 22210
第12层 3
备注:
对于100%的数据,
0 < T <= 100
0 < n <= 10^7
链接:https://www.nowcoder.com/acm/contest/67/J
思路
规律题。00->1、111->2、2222->3、33333->4、444444->5、5555555->6……
先把每一层的所有数字变成数字0的个数,再由数字0合成其他数字。有一个原则是,能合成多大数字,就先合多大数字。
代码

#include <bits/stdc++.h>
using namespace std;
LL b[10];//数字i需要多少bi合成
int main()
{
int T;
b[0]=1;
for(int i=1;i<10;i++){b[i]=b[i-1]*(i+1);}
cin>>T;
while(T--)
{
LL n;
cin>>n;
LL num=n+n/2*2;//0的数量
for(int i=9;i>=0;i--){
if(num/b[i]>=1){
for(int j=0;j<num/b[i];j++){
cout<<i;
}
}
num%=b[i];
}
cout<<endl;
}
return 0;
}