2016 CSP-J/NOIP万字长文复赛真题题解——秒杀T1 买铅笔,T2 回文日期,T3 海港,T4 魔法阵

[NOIP2016 普及组] 买铅笔

题干

[NOIP2016 普及组] 买铅笔

题目背景

NOIP2016 普及组 T1

题目描述

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

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

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

输入格式

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

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

保证所有的 777 个数都是不超过 100001000010000 的正整数。

输出格式

111 个整数,表示 P 老师最少需要花费的钱。

样例 #1

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

样例 #2

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

样例 #3

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

数据范围

保证所有的 777 个数都是不超过 100001000010000 的正整数。

子任务

子任务会给出部分测试数据的特点。如果你在解决题目中遇到了困难,可以尝试只解决一部分测试数据。

每个测试点的数据规模及特点如下表:

2016 CSP-J/NOIP万字长文复赛真题题解——秒杀T1 买铅笔,T2 回文日期,T3 海港,T4 魔法阵_第1张图片

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

思路

一共有三种铅笔,并且只能有一种铅笔。这个运算量非常小,所以我们可以直接暴力枚举,枚举每一种包装铅笔需要多少钱,取最小值即可。
怎么计算需要多少包铅笔呢?只要用总共需要的铅笔数除以一包这种包装的铅笔数,如果有余数就再加1,就可以保证能够满足要求,再用需要的包数乘以一包的单价即可求出买这一种铅笔的总价

看样例:
铅笔的三种包装分别是:

  • 222 支装,价格为 222;
  • 505050 支装,价格为 303030;
  • 303030 支装,价格为 272727
    P 老师需要购买至少 575757 支铅笔。
    如果她选择购买第一种包装,那么她需要购买 292929 份,共计 2×29=582 \times 29 = 582×29=58 支,需要花费的钱为 2×29=582 \times 29 = 582×29=58
    实际上,P 老师会选择购买第三种包装,这样需要买 222 份。虽然最后买到的铅笔数量更多了,为 30×2=6030 \times 2 = 6030×2=60 支,但花费却减少为 27×2=5427 \times 2 = 5427×2=54,比第一种少。
    对于第二种包装,虽然每支铅笔的价格是最低的,但要够发必须买 222 份,实际的花费达到了 30×2=6030 \times 2 = 6030×2=60,因此 P 老师也不会选择。
    所以最后输出的答案是 545454

时间复杂度

O(1)O(1)O(1)

空间复杂度

O(1)O(1)O(1)

代码

#include 
using namespace std ;
int n,a,b,ans = INT_MAX ;
int main(){
   
   
    scanf("%d",&n) ;
    for(int i = 0;i < 3;++ i){
   
   
        scanf("%d%d",&a,&b) ;
        ans = min(ans,((n / a) + int((n % a) != 0)) * b) ;
    }
    printf("%d",ans) ;
    return 0 ;
}

[NOIP2016 普及组] 回文日期

题干

题目背景

NOIP2016 普及组 T2

题目描述

在日常生活中,通过年、月、日这三个要素可以表示出一个唯一确定的日期。

牛牛习惯用 888 位数字表示一个日期,其中,前 444 位代表年份,接下来 222 位代表月份,最后 222 位代表日期。显然:一个日期只有一种表示方法,而两个不同的日期的表 示方法不会相同。

牛牛认为,一个日期是回文的,当且仅当表示这个日期的 888 位数字是回文的。现在,牛牛想知道:在他指定的两个日期之间包含这两个日期本身),有多少个真实存在的日期是回文的。

一个 888 位数字是回文的,当且仅当对于所有的 iii1≤i≤81 \le i \le 81i8)从左向右数的第 iii 个数字和第 9−i9-i9i 个数字(即从右向左数的第 iii 个数字)是相同的。

例如:

  • 对于 2016 年 11 月 19 日,用 888 位数字 201611192016111920161119 表示,它不是回文的。
  • 对于 2010 年 1 月 2 日,用 888 位数字 201001022010010220100102 表示,它是回文的。
  • 对于 2010 年 10 月 2 日,用 888 位数字 201010022010100220101002 表示,它不是回文的。

每一年中都有 121212 个月份:

其中,1,3,5,7,8,10,121, 3, 5, 7, 8, 10, 121,3,5,7,8,10,12 月每个月有 313131 天;4,6,9,114, 6, 9, 114,6,9,11 月每个月有 303030 天;而对于 222 月,闰年时有 292929 天,平年时有 282828 天。

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

  1. 这个年份是 444 的整数倍,但不是 100100100 的整数倍;
  2. 这个年份是 400400400 的整数倍。

例如:

  • 以下几个年份都是闰年:2000,2012,20162000, 2012, 20162000,2012,2016
  • 以下几个年份是平年:1900,2011,20141900, 2011, 20141900,2011,2014

输入格式

两行,每行包括一个 888 位数字。

第一行表示牛牛指定的起始日期。

第二行表示牛牛指定的终止日期。

保证 date1\mathit{date}_1date1date2\mathit{date}_2date2 都是真实存在的日期,且年份部分一定为 444 位数字,且首位数字不为 000

保证 date1\mathit{date}_1date1 一定不晚于 date2\mathit{date}_2date2

输出格式

一个整数,表示在 date1\mathit{date}_1date1date2\mathit{date}_2date2 之间,有多少个日期是回文的。

样例 #1

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

样例 #2

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

【子任务】

对于 60%60 \%60% 的数据,满足 date1=date2\mathit{date}_1 = \mathit{date}_2date1=date2

思路

普通思路

枚举两个日期之间的每一天,然后判断有多少个回文数。
用一个数组记录下每个月份的天数,如果是闰年的话,就把2月改成29天,否则改成28天。
如果天数超过了这个月的最大天数,天数归一,月份数加一。
如果月份数超过了12,月份数归一,年数加一。
对于样例 1,符合条件的日期是 201111022011110220111102
对于样例 2,符合条件的日期是 200110022001100220011002201001022010010220100102
具体解析看代码

时间复杂度

n=两个日期相差的天数n = 两个日期相差的天数n=两个日期相差的天数
O(n)O(n)O(n)

空间复杂度

O(1)O(1)O(1)

代码

#include 
using namespace std ;
int start,e,s ;
int a[] = {
   
   0,31,28,31,30,31,30,31,31,30,31,30,31} ;
//不能用end,因为end在一些编译器上会编译错误 
bool run(int date){
   
   
	//判断是否是闰年 
    int year = date / 10000 ; 
    if((year % 100) == 0)return (year % 400) == 0 ;
    return (year % 4) == 0 ; 
}
void new_day(int& date){
   
   
    if(run(date))a[2] = 29 ;
    //如果是闰年,把二月天数改成29 
    else a[2] = 28 ;
    //否则,把二月天数改成28 
    int year = date / 10000,day = date % 100,month = date / 100 % 100 ;
    //去除年、月、日 
    day ++ ;
    //天数加一 
    if(day > a[month])month ++ , day = 1 ;
    //如果超过了这个月的最大天数,月数加一 
    if(month > 12)month = 1 , year ++ ;
    //如果超过了12个月,年数加一 
    date = year * 10000 + month * 100 + day ;
    //重新拼接成原来的格式 
    return ;
}
bool hw(int num){
   
   
	//判断是否是回文数 
    int t = 0,k = num ;
    while(num)t = t * 10 + num % 10 , num /= 10 ;
    return t == k ;
}
int main(){
   
   
    scanf("%d%d",&start,&e) ;
    while(start <= e){
   
   
        s += hw(start) ;
        //判断当前日期是否为答案 
        new_day(start

你可能感兴趣的:(2016 CSP-J/NOIP万字长文复赛真题题解——秒杀T1 买铅笔,T2 回文日期,T3 海港,T4 魔法阵)