NOIP2016复赛解题报告及考后总结

来源:互联网 发布:数据库常用函数 编辑:程序博客网 时间:2024/05/16 12:34

一、题目及分析

买铅笔

题目描述

P老师需要去商店买n支铅笔作为小朋友们参加NOIP的礼物。她发现商店一共有 3种包装的铅笔,不同包装内的铅笔数量有可能不同,价格也有可能不同。为了公平起 见,P老师决定只买同一种包装的铅笔。

商店不允许将铅笔的包装拆开,因此P老师可能需要购买超过n支铅笔才够给小朋友们发礼物。

现在P老师想知道,在商店每种包装的数量都足够的情况下,要买够至少n支铅笔*最少*需要花费多少钱。

输入格式:

输入的第一行包含一个正整数n,表示需要的铅笔数量。

接下来三行,每行用两个正整数描述一种包装的铅笔:其中第一个整数表示这种包装内铅笔的数量,第二个整数表示这种包装的价格。

保证所有的7个数都是不超过10000的正整数。

输出格式:

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

样例1输入:

57

2 2

50 30

30 27

样例1输出:

54


说明

铅笔的三种包装分别是:

•2支装,价格为2;

•50支装,价格为30

;•30支装,价格为27。

P老师需要购买至少57支铅笔。

如果她选择购买第一种包装,那么她需要购买29份,共计2x29 = 58支,需要花费的钱为2x29 = 58。

实际上,P老师会选择购买第三种包装,这样需要买2份。虽然最后买到的铅笔数 量更多了,为30x2 = 60支,但花费却减少为27 x2 = 54,比第一种少。

对于第二种包装,虽然每支铅笔的价格是最低的,但要够发必须买2份,实际的 花费达到了 30 x 2 = 60,因此P老师也不会选择。所以最后输出的答案是54。


样例2输入:

9998

128 233

128 2333

128 666

样例2输出:

18407

样例3输入:

9999

101 1111

1 9999

1111 9999

样例3输出:

89991

【子任务】子任务会给出部分测试数据的特点。如果你在解决题目中遇到了困难,可以尝试只解决一部分测试数据。每个测试点的数据规模及特点如下表:



上表中“整倍数”的意义为:若为“V”,表示对应数据所需要的铅笔数量n定是每种包装铅笔数量的整倍数(这意味着一定可以不用多买铅笔)。

分析:

这道题比较简单,直接用除法,再向上取整,乘上单价,找出最小的数。

#include<cstdio>#include<cmath>int main(){int n,i,x,y,sum=1<<30,a,b;scanf("%d",&n);for(i=1;i<=3;i++){scanf("%d%d",&x,&y);if(x!=0){a=int(1.0*n/x+0.9999999);b=a*y;if(b<sum)sum=b;}}printf("%d",sum);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。

分析:
最开始觉得是一道很坑的题目,就只判断了date1=date2的情况,后来发现可以直接枚举年数,结果忘了判断闰年,

可能要错一个点,要是考试时把“28”写成“29”就好了。。。(希望数据水一点)

#include<cstdio>#include<cstring>int mon[15]={0,31,28,31,30,31,30,31,31,30,31,30,31};//就是这里int main(){char a[15],b[15];int i,j,f,sf,x,y,z,w,month,day,sum=0;scanf("%s\n%s",a,b);f=0;for(i=0;i<8;i++){if(a[i]!=b[i]){f=1;break;}}if(f==0){sf=0;for(i=0;i<8;i++){if(a[i]!=a[7-i]){sf=1;break;}}if(sf==0){printf("1");return 0;}else{printf("0");return 0;}}else{x=1000*(a[0]-48)+100*(a[1]-48)+10*(a[2]-48)+a[3]-48;z=1000*(a[4]-48)+100*(a[5]-48)+10*(a[6]-48)+a[7]-48;y=1000*(b[0]-48)+100*(b[1]-48)+10*(b[2]-48)+b[3]-48;w=1000*(b[4]-48)+100*(b[5]-48)+10*(b[6]-48)+b[7]-48;for(i=x;i<=y;i++){if(i%10<2){month=i%10*10+i/10%10;day=i/100%10*10+i/1000%10;if(month<=12){if(mon[month]>=day){if(i==x){if(100*month+day>=z)sum++;}else if(i==y){if(100*month+day<=w)sum++;}else sum++;}}}}printf("%d",sum);}}


海港

题目描述

小K是一个海港的海关工作人员,每天都有许多船只到达海港,船上通常有很多来自不同国家的乘客。

小K对这些到达海港的船只非常感兴趣,他按照时间记录下了到达海港的每一艘船只情况;对于第i艘到达的船,他记录了这艘船到达的时间ti (单位:秒),船上的乘 客数星ki,以及每名乘客的国籍 x(i,1), x(i,2),…,x(i,k);。

小K统计了n艘船的信息,希望你帮忙计算出以每一艘船到达时间为止的24小时(24小时=86400秒)内所有乘船到达的乘客来自多少个不同的国家。

形式化地讲,你需要计算n条信息。对于输出的第i条信息,你需要统计满足 ti - 86400 < tp <= ti的船只p,在所有的x(p,j)中,总共有多少个不同的数。


样例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秒到达海港,最近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个不同的国家。

【样例二说明】

第一艘船在第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个不同的国家。


分析:
在考试的时候,觉得这道题很坑,结果考完之后才知道是一道水题。

这道题既容易超时,也容易爆内存,最好的方法就是在原来的基础上进行统计。

用一个结构体队列来存一个人的国籍和到达时间,再用一个头指针来判断前面的人到达时间是否超过24小时。

结果我在考试的时候,是用一个vis数组来判重,每次输入一条船,就进行一次判重,最后就超时了。。。

AC代码:

#include<cstdio>#include<queue>#include<algorithm>using namespace std;struct node{    int x,time;}o,t;queue <node>a;int v[100005];int main(){int i,m,j,n,k,s=0;    scanf("%d",&n);    for(i=1;i<=n;i++){        scanf("%d%d",&t.time,&m);        for(j=1;j<=m;j++){scanf("%d",&t.x);            a.push(t);            v[t.x]++;            k=max(k,t.x);            if(v[t.x]==1)                s++;        }o=a.front();while(t.time-o.time>=86400){a.pop();v[o.x]--;if(!v[o.x])s--;o=a.front();}printf("%d\n",s);    }}






魔法阵



样例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

【样例解释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个数之和都应等于魔法阵的总数。所以,如果你的输出不满足这个性质,那么这个输出一定不正确。你可以通过这个性质在一定程度上检查你的输出的正确性。

样例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

【子任务】
每个测试点的详细数据范围见下表


分析:
一看这道题,就觉得是一道很水的题,但是看了数据范围就发现这道题很坑,虽然正解是用枚举,但是枚举
技巧的。然而............在考试的时候,我用了4个for循环,超时了一堆点。
听说正解是用2个for循环,但是我现在还没有弄明白用2个for循环的思路,如果有哪位高人知道,请在评论里面说
一下。





二、考后心得
经过了这一次NOIP的考试,我觉得NOIP不会考太难的算法,大部分的题目都是考(思维能力+一些简单算法),
只有后面的3题或4题比较难以外,其它的题目都比较正常,但是在考试的时候一定要仔细,有时候数据很坑,
但是样例很简单,你测样例就测不出程序的BUG,所以你就要多编几组特殊数据,尽量卡爆自己的程序,
这样你就可以找出程序的漏洞,从而改进自己的程序,
如果你发现自己的程序在测极端数据时会超时,就想一下其它的方法,比如说第3题,我没有在原来的基础上进行
统计,就超时了。
一些涉及到思维难度的题目,比如说第4题,如果你实在想不出来改进的思路,就去检查一下前面的题,保证自己
在前面的题目上不丢分。
以上就是我对NOIP考试的心得。

1 0