【NOIP2016普及组】解题报告 买铅笔 回文日期 海港 魔法阵

测题网站公示

大家可以测一下,算一下平均分。
这是我第一次参加NOIP竞赛。
经过了这次考试,我更加明白了骗分的含义。。。
题目下载链接
cena评测包下载
官方评测数据包


总结

考前做了足够的准备吧,每天复习一下知识点,重做经典的例题什么的~
然后考试时觉得自己的状态良好吧,能够编写出符合题意的程序,就是觉得时间不够,没有去一道一道的去编写两个程序对拍。
事实上,我觉得这次考试稍微有一点水,但是我还是有一些题该骗到的分没有被骗到(或者做出来)
所以测下来才230分。
我觉得我需要再提高的是我的骗分技巧。


题目详解

买铅笔

原题

题目描述

P老师需要去商店买n支铅笔作为小朋友们参加NOIP的礼物。她发现商店一共有3种包装的铅笔,不同包装内的铅笔数量有可能不同,价格也有可能不同。为了公平起见,P老师决定只买同一种包装的铅笔。
商店不允许将铅笔的包装拆开,因此P老师可能需要购买超过n支铅笔才够给小朋友们发礼物。
现在P老师想知道,在商店每种包装的数量都足够的情况下,要买够至少n支铅笔最少需要花费多少钱。

输入输出格式
输入格式:

输入的第一行包含一个正整数n,表示需要的铅笔数量。
接下来三行,每行用两个正整数描述一种包装的铅笔:其中第一个整数表示这种包装内铅笔的数量,第二个整数表示这种包装的价格。
保证所有的7个数都是不超过10000的正整数。

输出格式:

输出一行一个整数,表示P老师最少需要花费的钱。

输入输出样例

输入样例#1:
57
2 2
50 30
30 27
输出样例#1:
54

输入样例#2:
9998
128 233
128 2333
128 666
输出样例#2:
18407

输入样例#3:
9999
101 1111
1 9999
1111 9999
输出样例#3:
89991

说明

铅笔的三种包装分别是:

  • 2支装,价格为2;
  • 50支装,价格为30;
  • 30支装,价格为27。

P老师需要购买至少57支铅笔。
如果她选择购买第一种包装,那么她需要购买29份,共计2x29=58支,需要花费的钱为2x29=58。
实际上,P老师会选择购买第三种包装,这样需要买2份。虽然最后买到的铅笔数量更多了,为30x2=60支,但花费却减少为27x2=54,比第一种少。
对于第二种包装,虽然每支铅笔的价格是最低的,但要够发必须买2份,实际的花费达到了30x2=60,因此P老师也不会选择。

所以最后输出的答案是54。

子任务

子任务会给出部分测试数据的特点。如果你在解决题目中遇到了困难,可以尝试只解决一部分测试数据。
每个测试点的数据规模及特点如下表:
子任务
上表中“整倍数”的意义为:若为“K”,表示对应数据所需要的铅笔数量n—定是每种包装铅笔数量的整倍数(这意味着一定可以不用多买铅笔)。

分析

初步鉴定:水题。
大概得分:100
表示只要把每一种笔都试一下,一直买到大于等于n就行了(因为题目中说了n和两个数都为正整数,所以不用判断为0的情况),从三个情况中选择最小的花费就行了。

源代码

我的代码
提示:请登录后进行操作。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int n,ans=0x7fffffff;
struct pencil{
    int a;
    int b;
}p[4];
int main()
{
    //freopen("pencil.in","r",stdin);
    //freopen("pencil.out","w",stdout);//文件的读入输出
    cin>>n;
    for(int i=1;i<=3;i++)
        cin>>p[i].a>>p[i].b;
    for(int i=1;i<=3;i++)
        if(int((n*1.0)/p[i].a+0.99999)*p[i].b<ans)
            ans=int((n*1.0)/p[i].a+0.99999)*p[i].b;//无限加(因为所有数都为正整数)
    cout<<ans;
    //fclose(stdin);
    //fclose(stdout);//关闭文件
    return 0;
}

回文日期

原题

在日常生活中,通过年、月、日这三个要素可以表示出一个唯一确定的日期。
牛牛习惯用8位数字表示一个日期,其中,前4位代表年份,接下来2位代表月份,最后2位代表日期。显然:一个日期只有一种表示方法,而两个不同的日期的表示方法不会相同。
牛牛认为,一个日期是回文的,当且仅当表示这个日期的8位数字是回文的。现在,牛牛想知道:在他指定的两个日期之间包含这两个日期本身),有多少个真实存在的日期是回文的。
一个8位数字是回文的,当且仅当对于所有的i(1<=i<=8)从左向右数的第i个数字和第9-i个数字(即从右向左数的第i个数字)是相同的。
例如:

  • 对于2016年11月19日,用8位数字20161119表示,它不是回文的。
  • 对于2010年1月2日,用8位数字20100102表示,它是回文的。
  • 对于2010年10月2日,用8位数字20101002表示,它不是回文的。

每一年中都有12个月份:
其中,1、3、5、7、8、10、12月每个月有31天;4、6、9、11月每个月有30天;而对于2月,闰年时有29天,平年时有28天。

一个年份是闰年当且仅当它满足下列两种情况其中的一种:

  1. 这个年份是4的整数倍,但不是100的整数倍;
  2. 这个年份是400的整数倍。

例如:

  • 以下几个年份都是闰年:2000、2012、2016。
  • 以下几个年份是平年:1900、2011、2014。
输入输出格式
输入格式:

输入包括两行,每行包括一个8位数字。
第一行表示牛牛指定的起始日期。
第二行表示牛牛指定的终止日期。
保证date_i和都是真实存在的日期,且年份部分一定为4位数字,且首位数字不为0。
保证date1—定不晚于date2。

输出格式:

输出一行,包含一个整数,表示在date1和date2之间,有多少个日期是回文的。

输入输出样例

输入样例#1:
20110101
20111231
输出样例#1:
1

输入样例#2:
20000101
20101231
输出样例#2:
2

说明
样例说明

对于样例1,符合条件的日期是20111102。
对于样例2,符合条件的日期是20011002和20100102。

子任务

对于60%的数据,满足date1=date2。

分析

初步鉴定:有点坑(考虑每个月的方面,和闰年的2月份的情况),但是应该得满分。
大概得分:60
这道题,我就是用的最笨的方法,就是用3个for循环把每一天的情况都列举出来,可是只过了60%,也就是题目中说的当两个日期相等的情况,幸好,至少没有拿0分。
这道题应该直接列举年份,出现与其相对的日期是否合法就行了。

源代码

我的代码
提示:请登录后进行操作。

#include<cstdio>
#include<iostream>
using namespace std;
int day[13]={0,31,29,31,30,31,30,31,31,30,31,30,31};
int A,B,ans,maxy,miny,maxm,minm,maxd,mind;

int check(int m,int d){
    int y;
    y=(d%10)*1000+(d/10)*100+(m%10)*10+(m/10);
    if(y>miny&&y<maxy)
        return 1;
    if(y==miny){
        if(m>minm)
            return 1;
        if(m==minm&&d>=mind)
            return 1;
    }
    if(y==maxy){
        if(m<maxm)
            return 1;
        if(m==maxm&&d<=maxd)
            return 1;
    }
    return 0;
}

int main()
{
    ans=0;
    cin>>A>>B;
    maxy=B/10000,maxm=(B%10000)/100,maxd=B%100;
    miny=A/10000,minm=(A%10000)/100,mind=A%100;
    for(int i=1;i<=12;i++)
        for(int j=1;j<=day[i];j++)
            ans+=check(i,j);
    cout<<ans;
}

海港

原题

题目描述

小K是一个海港的海关工作人员,每天都有许多船只到达海港,船上通常有很多来自不同国家的乘客。
小K对这些到达海港的船只非常感兴趣,他按照时间记录下了到达海港的每一艘船只情况;对于第i艘到达的船,他记录了这艘船到达的时间 ti (单位:秒),船上的乘客数量 ki ,以及每名乘客的国籍 xi,1,xi,2,,xi,k
小K统计了n艘船的信息,希望你帮忙计算出以每一艘船到达时间为止的24小时(24小时=86400秒)内所有乘船到达的乘客来自多少个不同的国家。
形式化地讲,你需要计算n条信息。对于输出的第i条信息,你需要统计满足 ti86400<tpti 的船只p,在所有的 xp,j 中,总共有多少个不同的数。

输入输出格式
输入格式:

第一行输入一个正整数n,表示小K统计了n艘船的信息。
接下来n行,每行描述一艘船的信息:前两个整数 ti ki 分别表示这艘船到达海港的时间和船上的乘客数量,接下来 ki 个整数 xi,j 表示船上乘客的国7。
保证输入的 ti 是递增的,单位是秒;表示从小K第一次上班开始计时,这艘船在第 ti 秒到达海港。
保证 1n105,ki1,ki3×105,1ti1ti109
其中表示所有的 ki 的和。

输出格式:

输出n行,第i行输出一个整数表示第i艘船到达后的统计信息。

输入输出样例

输入样例#1:
3
1 4 4 1 2 2
2 2 2 3
10 1 3
输出样例#1:
3
4
4

输入样例#2:
4
1 4 1 2 2 3
3 2 2 3
86401 2 3 4
86402 1 5
输出样例#2:
3
3
3
4

说明
样例解释1

第一艘船在第1秒到达海港,最近24小时到达的船是第一艘船,共有4个乘客, 分别是来自国家4,1,2,2,共来自3个不同的国家;
第二艘船在第2秒到达海港,最近24小时到达的船是第一艘船和第二艘船,共有 4 + 2 = 6个乘客,分别是来自国家4,1,2,2,2,3,共来自4个不同的国家;
第三艘船在第10秒到达海港,最近24小时到达的船是第一艘船、第二艘船和第三艘船,共有4+ 2+1=7个乘客,分别是来自国家4,1,2,2,2,3,3,共来自4个不同 的国家。

样例解释2

第一艘船在第1秒到达海港,最近24小时到达的船是第一艘船,共有4个乘客,分别是来自国家1,2,2,3,共来自3个不同的国家。
第二艘船在第3秒到达海港,最近24小时到达的船是第一艘船和第二艘船,共有4+2=6个乘客,分别是来自国家1,2,2,3,2,3,共来自3个不同的国家。
第三艘船在第86401秒到达海港,最近24小时到达的船是第二艘船和第三艘船,共有2+2=4个乘客,分别是来自国家2,3,3,4,共来自3个不同的国家。
第四艘船在第86402秒到达海港,最近24小时到达的船是第二艘船、第三艘船和第四艘船,共有2+2+1=5个乘客,分别是来自国家2,3,3,4,5,共来自4个不同的国家。

子任务

分析

初步鉴定:一般般
大概得分:70
这道题,听他们说要用队列vector来做,我只是用最笨的方法int数组+for循环一道判断在不在86400秒内,如果在就累加起来。所以会超时,幸好对于70%的测试点, 1n1000ki3000;1xi,j1000;1ti109 否则就0分了。

源代码

我的代码
提示:请登录后进行操作。

#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<queue>
using namespace std;
int x[100001],n,sum;
struct node
{
    int t,p;
};
queue <node> h;

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int t,p;
        scanf("%d%d",&t,&p);
        for(int j=1;j<=p;j++){
            node now;
            int g;
            scanf("%d",&g);
            now.t=t;now.p=g;
            h.push(now);
            x[g]++;
            if(x[g]==1) sum++;
        }
        node a=h.front();
        node now=h.back();
        while(a.t<=now.t-86400)
        {
            x[a.p]--;if(x[a.p]==0) sum--;
            h.pop();
            a=h.front();
        }
        printf("%d\n",sum);
    }
}

魔法阵

原题

题目描述

六十年一次的魔法战争就要开始了,大魔法师准备从附近的魔法场中汲取魔法能量。
大魔法师有m个魔法物品,编号分别为1,2,…,m。每个物品具有一个魔法值,我们用 xi 表示编号为i的物品的魔法值。每个魔法值 xi 是不超过n的正整数,可能有多个物品的魔法值相同。
大魔法师认为,当且仅当四个编号为a,b,c,d的魔法物品满足 xa<xb<xc<xdxbxa=2(xdxc) ,并且 xbxa<(xcxb)/3 时,这四个魔法物品形成了一个魔法阵,他称这四个魔法物品分别为这个魔法阵的A物品,B物品,C物品,D物品。
现在,大魔法师想要知道,对于每个魔法物品,作为某个魔法阵的A物品出现的次数,作为B物品的次数,作为C物品的次数,和作为D物品的次数。

输入输出格式
输入格式:

输入文件的第一行包含两个空格隔开的正整数n和m。
接下来m行,每行一个正整数,第i+1行的正整数表示Xi,即编号为i的物品的魔法值。
保证 1n15000,1m40000,1xin 。每个 xi 是分别在合法范围内等概率随机生成的。

输出格式:

共输出m行,每行四个整数。第i行的四个整数依次表示编号为i的物品作 为A,B,C,D物品分别出现的次数。
保证标准输出中的每个数都不会超过 109
每行相邻的两个数之间用恰好一个空格隔开。

输入输出样例

输入样例#1:
30 8
1
24
7
28
5
29
26
24
输出样例#1:
4 0 0 0
0 0 1 0
0 2 0 0
0 0 1 1
1 3 0 0
0 0 0 2
0 0 2 2
0 0 1 0

输入样例#2:
15 15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
输出样例#2:
5 0 0 0
4 0 0 0
3 5 0 0
2 4 0 0
1 3 0 0
0 2 0 0
0 1 0 0
0 0 0 0
0 0 0 0
0 0 1 0
0 0 2 1
0 0 3 2
0 0 4 3
0 0 5 4
0 0 0 5

说明
样例解释1

共有5个魔法阵,分别为:
物品1,3,7,6,其魔法值分别为1,7,26,29;
物品1,5,2,7,其魔法值分别为1,5,24,26;
物品1,5,7,4,其魔法值分别为1,5,26,28;
物品1,5,8,7,其魔法值分别为1,5,24,26;
物品5,3,4,6,其魔法值分别为5,7,28,29。
以物品5为例,它作为A物品出现了1次,作为B物品出现了3次,没有作为C物品或者D物品出现,所以这一行输出的四个数依次为1,3,0,0。
此外,如果我们将输出看作一个m行4列的矩阵,那么每一列上的m个数之和都应等于魔法阵的总数。所以,如果你的输出不满足这个性质,那么这个输出一定不正确。你可以通过这个性质在一定程度上检查你的输出的正确性。

分析

初步鉴定:坑爹
大概得分:0
这道题可谓是最终的压轴题,这道题我在考试时就是用4个for循环来做的,想多骗一点分,结果一分都木有骗掉,香菇蓝瘦/(ㄒoㄒ)/~~
于是乎,并没有骗到一分XAX,在考试时我可用了40分钟来做啊(好吧,某种意义叫骗分吧)!

源代码

我的代码
提示:请登录后进行操作。

#include<cstdio>
#include<iostream>
using namespace std;
int a[15005],b[15005],c[15005],d[15005],w[15005];
int h[40005];
int f[15005];
int n,m,k,t,x,y,z,p,q;
int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        cin>>h[i];
        w[h[i]]++;
    }
    for(int i=1;i<=n/9;i++){
        x=1+9*i;y=0;
        for(int j=2+9*i;j<=n;j++){
            y=y+w[(j-x)]*w[j-x+i+i];
            d[j]=d[j]+y*w[j-i];
            c[j-i]=c[j-i]+y*w[j];
        }
        x=8*i+1;y=0;
        for(int j=n-9*i-1;j>=1;j--){
            y=y+w[j+x]*w[j+x+i];
            a[j]=a[j]+y*w[j+i+i];
            b[j+i+i]=b[j+i+i]+y*w[j];
        }
    }
    for(int i=1;i<=m;i++)
        cout<<a[h[i]]<<" "<<b[h[i]]<<" "<<c[h[i]]<<" "<<d[h[i]]<<endl;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值