2018年全国多校算法寒假训练营练习比赛(第一场) - (A,B,F,I,J)

这场比赛WA比较多,还是题意,先读懂题意再做题。而且读题目下面的样例说明是很重要的。

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

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

题意:

每一把枪都有其威力及其可装备配件种类。每一个配件有其所属种类,可以为枪支提供威力的百分比加成。每一把枪只能装备一个同类配件。给你n把枪支和m个配件,枪的威力为p,可装备的配件数量为k,为k个不同类型的配件,同种类配件只可以装备一个。配件种类用数字q表示,配件威力加成用一个小数b表示。请你挑选一把枪并为其搭配配件使其威力最大。

    假设一把枪的威力是p,装配的k个配件的威力加成是bi,那么枪最后的威力w=p*(1+b1+b2+…+bk)。

分析:

    注意不同类型配件只需保留威力加成最大的一个即可。

代码:

#include<iostream>
#include<string>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<math.h>
#include<iomanip>
#include<queue>
#include<cstring>
#include<map>
using namespace std;
typedef long long ll;
#define M 1005
int n,m;
double qb[M];
struct node{
    double p;
    int k;
    int num[M];
}gun[M];

int main()
{
    int i,j,q;
    double b;

    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(i=1;i<=n;i++)
        {
            scanf("%lf%d",&gun[i].p,&gun[i].k);
            for(j=1;j<=gun[i].k;j++)
            {
                scanf("%d",&gun[i].num[j]);
            }
        }
        for(i=0;i<M;i++)
            qb[i]=0.0;
        for(i=1;i<=m;i++) //qb[i]代表i型配件的最大威力
        {
            scanf("%d%lf",&q,&b);
            qb[q]=max(qb[q],b);
        }
        double tmp=0,ans=0;
        for(i=1;i<=n;i++)
        {
            tmp=1.0;
            for(j=1;j<=gun[i].k;j++)
                tmp+=qb[gun[i].num[j]];
            tmp=tmp*gun[i].p; //这里的tmp是枪i的满配威力
            ans=max(ans,tmp);
        }
        cout<<fixed<<setprecision(4)<<ans<<endl;
    }
    return 0;
}

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


题目描述

      在游戏中玩家L要出n张牌,每一张牌都有其发动速度以及效果。如果后发动的一张牌的发动速度不小于前一张牌,则后发动的那张牌会在前一张牌后进行连锁发动,这张牌的连锁数就是连锁发动的编号。不进行连锁发动的牌,连锁数为1。同一连锁中的牌,后发动的牌先生效。

    如有连续5张牌的速度分别为(1,2,2,2,3),因此它们进行连锁发动。对应连锁数分别为(1,2,3,4,5),因为连锁中的牌,后发动的先生效,所以,生效顺序为(5,4,3,2,1)。

    为简化问题,我们假设发动的牌的效果有如下几种:

    1. 对对方造成X点伤害

    2. 对对方造成这张牌的连锁数乘X点的伤害

    3. 同一连锁中的牌全部无效

    4. 连锁中的前一张牌无效

    现在你知道L发动牌的效果、速度和顺序,求L能对对方造成多少伤害。

输入

9
1 1 300
2 2 400
2 3
2 2 500
1 1 1000
3 4
2 1 600
3 3
3 4

输出

2600

说明

一共发动了9张牌,前四张为一次连锁发动,第四张先生效,连锁数为4,造成4*500伤害。接着第三张牌生效,使第一二张牌无效。第五六张牌为一次连锁发动,第六张牌先生效,使其连锁的前一张牌,即第五张牌无效。第七八九张牌为一次连锁发动,第九张牌先生效,使连锁中的前一张牌,即第八张牌无效。接着第七张牌生效,造成600伤害。
总伤害为4*500+600=2600伤害。

分析:

题目关键在于对牌分组,分组后按组处理,注意:效果3. 同一连锁中的牌全部无效,是只对其之后连锁的牌生效,之前已经生效的攻击不会取消。

代码:

#include<iostream>
#include<string>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<math.h>
#include<iomanip>
#include<queue>
#include<cstring>
#include<map>
using namespace std;
typedef long long ll;
#define M 1005
struct node{
    int s,t,x;
}p[M];

int main()
{
    int i,n;

    p[0].s=0;

    while(scanf("%d",&n)!=EOF)
    {
        for(i=1;i<=n;i++)
        {
            scanf("%d%d",&p[i].s,&p[i].t);
            if(p[i].t==1||p[i].t==2)
                scanf("%d",&p[i].x);
        }

        ll ans=0;
        int it=1,beg=0; //it是当前处理到哪一张牌,beg是上一组牌的最后一张
        while(it<=n)
        {
            if(p[it].s>=p[it-1].s)
            {
                it++;
            }else{  //如果s下降,那么出现新的一组,这里的it代表的就是下一组牌的第一张,
                for(i=it-1;i>beg;i--)
                {
                    if(p[i].t==3)
                    {
                        break;
                    }else if(p[i].t==4)
                    {
                        i--;
                    }else if(p[i].t==2)
                    {
                        ans+=(ll)(i-beg)*(p[i].x);
                    }else
                    {
                        ans+=(ll)p[i].x;
                    }
                }
                beg=it-1; 
                it++;   //令it指向本组第一张牌的下一张
            }
        }
        if(it>n) //注意可能有最后一组在while循环中还未处理
        {
            for(i=it-1;i>beg;i--)
            {
                if(p[i].t==3)
                {
                    break;
                }else if(p[i].t==4)
                {
                    i--;
                }else if(p[i].t==2)
                {
                    ans+=(ll)(i-beg)*(p[i].x);
                }else
                {
                    ans+=(ll)p[i].x;
                }
            }
        }

        cout<<ans<<endl;
    }
    return 0;
}

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

题目描述 

    现在有一款很火的游戏playerunknown's battlegrounds,人称“吃鸡”,在里面经常面临跑毒(从安全区外跑进安全区内)的问题,在安全区外,人们会处于中毒状态,每秒会掉a%血,人们可以通过使用道具急救包把血量升回到80%,使用急救包需要原地站着6秒。现在知道在安全区外扣血速度为a%/s,角色和安全区的距离为b米,角色跑步速度为1m/s,角色有c个急救包,请问角色是否能安全跑进安全区内。(PS:角色开始的血量为100%。如果血量降到0%,立刻判定为死亡。使用急救包时,如果刚使用完毕瞬间或者正在使用急救包的时候,血量降到0%,角色立即判定为死亡。顺带一提,这里判断时间不存在0.xxxx秒,最小时间单位为1s)

输入描述:

第一行是样例数T(T<9)
第2到2+T-1行每行有三个整数a b c,其中a为安全区外的扣血速度a%/s,b为角色和安全区的距离,c为急救包的数量。

输出描述:

如果角色能进入安全区输出“YES”。
若角色在安全区外死亡输出“NO”。
示例1

输入

3
1 100 2
6 31 2
7 31 2

输出

YES
YES
NO

说明

当a=6,b=31,c=2时,
0s起跑,10s的时候角色跑了10M,血量剩下40%,开始使用急救包,16s的时候,角色血量先降到4%再恢复到80%,然后角色继续跑步。23s的时候,角色跑了17M,剩余血量为38%,开始使用急救包,29s的时候,角色血量先降到2%再恢复到80%,然后角色继续跑步。42s的时候,角色跑了30m,血量剩余2%。当43s的时候,角色跑了31m进入了安全区内,不再扣血。
分析:

这个题目,只要读懂样例说明就好,否则会弄错减血,加血,抵达,前进之间的顺序

这里正确的顺序是:每过一秒,先前进一米,然后减血,然后加血。

代码:

#include<iostream>
#include<string>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<math.h>
#include<iomanip>
#include<queue>
#include<cstring>
#include<map>
using namespace std;
typedef long long ll;
#define M 1005
int main()
{
    int T,a,b,c;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&a,&b,&c);
        int blood=100;
        int dis=b;
        bool f=true;
        while(1)
        {
            dis--;
            if(dis==0)
            {
                break;
            }

            blood-=a;
            if(blood<=0)
            {
                f=false;
                break;
            }
            if(blood-6*a<=a)
            {
                if(c>0)
                {
                    c--;
                    blood=80;
                }
            }
            //blood-=a;
        }
        if(f)
            cout<<"YES"<<endl;
        else
            cout<<"NO"<<endl;
    }
    return 0;
}

I 找数字个数

题意:

题目其实是求,1~1000这1000个数字中,有多少个数字满足:这个数字既不是a或b的倍数,它对应10进制的每一位也不包含a或b对应10进制的任何一位。

代码 :
#include<iostream>
#include<string>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<math.h>
#include<iomanip>
#include<queue>
#include<cstring>
#include<map>
using namespace std;
typedef long long ll;
#define M 100
int aa[10],bb[10],num[10];

void init(int n,int cnt[]) //将n按10进制分解放入数组cnt中
{
    int tot=0;
    while(n!=0)
    {
        cnt[++tot]=n%10;
        n=n/10;
    }
    cnt[0]=tot;
}

int main()
{
    int a,b,T,i,j,k;

    scanf("%d",&T);

    while(T--)
    {
        scanf("%d%d",&a,&b);
        init(a,aa);
        init(b,bb);
        int ans=0;
        for(i=1;i<=1000;i++)
        {
            if(i%a==0||i%b==0)
            {
                continue;
            }
            init(i,num);
            bool f=true;
            for(j=1;j<=num[0];j++)
            {
                for(k=1;k<=aa[0];k++)
                {
                    if(num[j]==aa[k])
                    {
                        f=false;
                        break;
                    }
                }
                if(f==false)
                    break;
                for(k=1;k<=bb[0];k++)
                {
                    if(num[j]==bb[k])
                    {
                        f=false;
                        break;
                    }
                }
                if(f==false)
                    break;
            }
            if(f)
                ans++;
        }

        cout<<ans<<endl;
    }
    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

输入描述:

第一行是样例数T(T<100)
第2到2+T-1行每行有一个整数n(0<n<=10^7)。

输出描述:

从大到小输出lulu到达第n层时身上的数字
示例1

输入

4
1
2
3
20

输出

0
11
110
32211
分析题意:每到一层会获得对应数量的0,1,但没说获得规律,这是本题最大难点。再读题: 当你身上某个数字达到一个特定的数量时,它们会合成为下一个数字,其实就是说当数字i到达一定的数量它们会转化为一个数字即i-1。

然后观察前12项就会发现,对于每到达一个奇数层是获得一个0,每到达一个偶数层获得一个0一个1。然后2个0合成一个1,3个1合成一个2......10个8合成一个9.

观察样例中20对应32211,其实就等于12+8即"3"+"2211",其实对于大于12的n都可以这样来合成,合成一个"3"需要n=12,合成一个"4"需要n=5*12,合成一个"i"需要n=(i+1)*合成一个"i-1".

对于n小于12的可以打表。

然后其实上述方法是很无脑的方法,听队里dalao说,其实1个"1"和1个"0"就相当于3个"0",即有如下权值"1"=2*"0","2"=6*"0",将数字"i"换成"0"对应的权值,这样稍微高大上一些。

代码:

#include<iostream>
#include<string>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<math.h>
#include<iomanip>
#include<queue>
#include<cstring>
#include<map>
using namespace std;
typedef long long ll;
#define M 10

int num[M];
string ans[20];

int main()
{
    int T,i,n;
    num[3]=12;
    for(i=4;i<=9;i++)
    {
        num[i]=num[i-1]*(i+1);
    }

    ans[1]="0";
    ans[2]="11";
    ans[3]="110";
    ans[4]="21";
    ans[5]="210";
    ans[6]="22";
    ans[7]="220";
    ans[8]="2211";
    ans[9]="22110";
    ans[10]="2221";
    ans[11]="22210";

    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        string cnt="";
        if(n>=num[9])
        {
            for(i=1;i<=n/num[9];i++)
            {
                cnt+='9';
            }
            n=n%num[9];
        }
        if(n>=num[8])
        {
            for(i=1;i<=n/num[8];i++)
            {
                cnt+='8';
            }
            n=n%num[8];
        }
        if(n>=num[7])
        {
            for(i=1;i<=n/num[7];i++)
            {
                cnt+='7';
            }
            n=n%num[7];
        }
        if(n>=num[6])
        {
            for(i=1;i<=n/num[6];i++)
            {
                cnt+='6';
            }
            n=n%num[6];
        }
        if(n>=num[5])
        {
            for(i=1;i<=n/num[5];i++)
            {
                cnt+='5';
            }
            n=n%num[5];
        }
        if(n>=num[4])
        {
            for(i=1;i<=n/num[4];i++)
            {
                cnt+='4';
            }
            n=n%num[4];
        }
        if(n>=num[3])
        {
            for(i=1;i<=n/num[3];i++)
            {
                cnt+='3';
            }
            n=n%num[3];
        }
        if(n!=0)
            cnt+=ans[n];
        cout<<cnt<<endl;
    }
    return 0;
}
权值法:

#include<iostream>
#include<cmath>
#include<fstream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<string>
#include<queue>
#include<vector>
#include<iomanip>
#include<map>
#define lson l,mid,t<<1
#define rson mid+1,r,t<<1|1
#define ll long long
using namespace std;
const int mx=1010;
const int inf=0x3f3f3f3f;
long long mo=1e9+7;
long long ans,k,t,t1,z,z1,T,n,m,xf,yf,xx,yy,zz,sum,q;
bool flag;
int a[1000010];
double max(double x,double y){if(x>y) return x;return y;}
long long min(long long x,long long y){if(x<y) return x;return y;}
ll power(ll a,ll n){ll ans=1;a=a%mo;while (n){if(n&1) ans=(ans*a)%mo;n>>=1;a=(a*a)%mo;}return ans;}
int main() {
    //ios::sync_with_stdio(false);
    int cas=1;
    ll i,j,mid,mi;
    flag=0;
    ll n,m;
    double d;
    a[0]=1;
    a[1]=2;
    for(i=2;i<100000;i++) a[i]=a[i-1]*(i+1);
    while(scanf("%lld",&T)!=EOF)
    {
        while(T--)
        {
            scanf("%lld",&n);
            if(n==1) {puts("0");continue;}
            xx=n;
            yy=n>>1;
            sum=xx+2*yy;
            i=0;
            while(a[i]<sum) i++;
            for(j=i-1;j>=0;j--)
            {
             t=sum/a[j];
             while(t--) printf("%d",j);
             sum%=a[j];
            }
            puts("");
        //if(flag) printf("YES\n");else printf("NO\n");
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值