NOIP2016普及组复赛解题报告

mac2022-06-30  25

买铅笔

【题目描述】 P老师需要去商店买n支铅笔作为小朋友们参加NOIP的礼物。她发现商店一共有 3种包装的铅笔,不同包装内的铅笔数量有可能不同,价格也有可能不同。为了公平起 见,P老师决定只买同一种包装的铅笔。 商店不允许将铅笔的包装拆开,因此P老师可能需要购买超过n支铅笔才够给小朋 友们发礼物。 现在P老师想知道,在商店每种包装的数量都足够的情况下,要买够至少n支铅笔最少需要花费多少钱。 【输入格式】 输入的第一行包含一个正整数n,表示需要的铅笔数量。 接下来三行,每行用两个正整数描述一种包装的铅笔:其中第一个整数表示这种 包装内铅笔的数量,第二个整数表示这种包装的价格。 保证所有的7个数都是不超过10000的正整数。 【输出格式】 输出一行一个整数,表示P老师最少需要花费的钱。 【样例输入】 57 2 2 50 30 30 27 【样例输出】 54 【分析】 水题,直接模拟即可。

var a,b,c,i,n,ans:longint; begin read(n); ans:=maxlongint; for i:=1 to 3 do begin readln(a,b); c:=n div a; if n mod a<>0 then inc(c); if b*c<ans then ans:=b*c; end; write(ans); end.

回文日期

【题目描述】 在日常生活中,通过年、月、日这三个要素可以表示出一个唯一确定的日期。 牛牛习惯用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之间,有多少个日期是回文的。 【样例输入】 20000101 20101231 【样例输出】 2 【分析】 回文数的前一半确定后,则整个回文数也就确定了。故枚举年份,直接可以得出与该年份构成回文的月和日,然后直接判断即可。

const run:array[1..12]of longint=(31,29,31,30,31,30,31,31,30,31,30,31); pin:array[1..12]of longint=(31,28,31,30,31,30,31,31,30,31,30,31); var date1,year1,mon1,day1,date2,year2,mon2,day2,year,mon,day,ans:longint; flag:boolean; function check_run(x:longint):boolean; begin if (x mod 400=0)or((x mod 4=0)and(x mod 100<>0)) then exit(true); exit(false); end; function make_huiwen(x:longint):longint; var s,s1:string; i:longint; begin str(x,s); s1:=''; for i:=length(s) downto 1 do s1:=s1+s[i]; val(s1,i); exit(i); end; begin read(date1);read(date2); year1:=date1 div 10000; mon1:=date1 mod 10000 div 100; day1:=date1 mod 100; year2:=date2 div 10000; mon2:=date2 mod 10000 div 100; day2:=date2 mod 100; ans:=0; for year:=year1 to year2 do begin flag:=false; mon:=make_huiwen(year) div 100; day:=make_huiwen(year) mod 100; if (mon<1)or(mon>12) then continue; if day<1 then continue; if not check_run(year) then begin if day>pin[mon] then continue; if year=year1 then if (mon>mon1) or ((mon=mon1)and(day>=day1)) then flag:=true; if year=year2 then if (mon<mon2) or ((mon=mon2)and(day<=day2)) then flag:=true; if (year1<year)and(year<year2) then flag:=true; end; if check_run(year) then begin if day>run[mon] then continue; if year=year1 then if (mon>mon1) or ((mon=mon1)and(day>=day1)) then flag:=true; if year=year2 then if (mon<mon2) or ((mon=mon2)and(day<=day2)) then flag:=true; if (year1<year)and(year<year2) then flag:=true; end; if flag then inc(ans); end; write(ans); end.

海港

【题目描述】 小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)中,总共有多少个不同的数。 【输入格式】 第一行输入一个正整数n,表示小K统计了 n艘船的信息。 接下来n行,每行描述一艘船的信息:前两个整数ti和ki分别表示这艘船到达海港的时间和船上的乘客数量,接下来ki个整数x(i,j)表示船上乘客的国籍。 保证输入的ti是递增的,单位是秒;表示从小K第一次上班开始计时,这艘船在第 ti 秒到达海港。 【输出格式】 输出n行,第i行输出一个整数表示第i艘船到达后的统计信息。 【样例输入】 3 1 4 4 1 2 2 2 2 2 3 10 1 3 【样例输出】 3 4 4 【数据范围】 对于70%的数据,1<=n<=1000,所有ki之和<=3000,1<=Xi,j<=1000,1<=ti<=10^9 对于100%的数据,1<=n<=100000,所有ki之和<=300000,1<=Xi,j<=100000,1<=ti<=10^9 【分析】 为了不爆空间,可以边读入边处理,只能开一维数组。

var n,i,sum,head,s,tot,j:longint; t,k,f:array[1..100000]of longint; x:array[1..300000]of longint; begin readln(n); read(t[1],k[1]); for i:=1 to k[1] do begin read(x[s+i]); inc(f[x[s+i]]); if f[x[s+i]]=1 then inc(sum); end; writeln(sum); s:=s+k[1];head:=1; for i:=2 to n do begin read(t[i],k[i]); for j:=1 to k[i] do begin read(x[s+j]); inc(f[x[s+j]]); if f[x[s+j]]=1 then inc(sum); end; s:=s+k[i]; while t[i]-86400>=t[head] do begin for j:=tot+1 to tot+k[head] do begin dec(f[x[j]]); if f[x[j]]=0 then dec(sum); end; tot:=tot+k[head]; inc(head); end; writeln(sum); end; end.

魔法阵

【题目描述】 六十年一次的魔法战争就要开始了,大魔法师准备从附近的魔法场中汲取魔法能量。 大魔法师有m个魔法物品,编号分别为1,2,…,m。每个物品具有一个魔法值,我们用Xi表示编号为i的物品的魔法值。每个魔法值Xi是不超过n的正整数,可能有多个物品的魔法值相同。 大魔法师认为,当且仅当四个编号为a,b,c,d的魔法物品满足xa< xb< xc< xd,Xb-Xa=2(Xd-Xc),并且xb-xa<(xc-xb)/3时,这四个魔法物品形成了一个魔法阵,他称这四个魔法物品分别为这个魔法阵的A物品,B物品,C物品,D物品。 现在,大魔法师想要知道,对于每个魔法物品,作为某个魔法阵的A物品出现的次数,作为B物品的次数,作为C物品的次数,和作为D物品的次数。 【输入格式】 第一行包含两个空格隔开的正整数n和m。 接下来m行,每行一个正整数,第i+1行的正整数表示Xi,即编号为i的物品的魔法值。 【输出格式】 共输出m行,每行四个整数。第i行的四个整数依次表示编号为i的物品作 为A,B,C,D物品分别出现的次数。 【样例输入】 30 8 1 24 7 28 5 29 26 24 【样例输出】 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<=n<=15000 1<=m<=40000 【分析】 这几乎就是一个数学问题。 令x=d-c(这里的c和d表示题目中的Xc和Xd,为了方便下文均使用下标代替)。 首先可以推出1<=x<=n div 9 然后由Xb-Xa=2(Xd-Xc)可以知道b-a=2x 得出a,代入xb-xa<(xc-xb)/3可得c-b>6x 不难得出b=a+2x,c=a+8x+k(这里的k是一个正整数),d=a+9x+k 于是直接枚举,然后就是统计咯。 注意:下面给的程序中的变量含义可能与分析中的不一样,请结合注释理解。

var n,m,i,j,x,y:longint; s,f,fa,fb,fc,fd:array[0..40001]of longint; begin read(n,m); for i:=1 to m do begin read(s[i]);inc(f[s[i]]); end; for i:=1 to n div 9 do begin //这个变量的含义不想多说了 x:=9*i+1;y:=0; //x记录d-a的最小值,即k=1;y记录有多少组符合条件的a和b(a和b看成一个整体) for j:=9*i+2 to n do begin //此处的j枚举的是d,确定c和d的个数 y:=y+f[j-x]*f[j-x+2*i]; fd[j]:=fd[j]+y*f[j-i]; fc[j-i]:=fc[j-i]+y*f[j]; end;//注意魔法值可能有重复 x:=8*i+1;y:=0; for j:=n-9*i-1 downto 1 do begin //现在确定a和b的个数 y:=y+f[j+x]*f[j+x+i]; fa[j]:=fa[j]+y*f[j+2*i]; fb[j+2*i]:=fb[j+2*i]+y*f[j]; end; end; for i:=1 to m do writeln(fa[s[i]],' ',fb[s[i]],' ',fc[s[i]],' ',fd[s[i]]); end.

总结:这次考试的AC代码几乎都很短,但是也很难写出来。纵观整套试题,几乎没有考到多少算法,全都是各种编程技巧和思维。所以从这一点来看,NOIP2016比前几年稍难一些。

转载于:https://www.cnblogs.com/JRX2015U43/p/6533483.html

相关资源:NOIP2016普及组初赛复赛真题及解答与测试数据
最新回复(0)