NOIP2016 普及组 T1
P 老师需要去商店买 nnn 支铅笔作为小朋友们参加 NOIP 的礼物。她发现商店一共有 333 种包装的铅笔,不同包装内的铅笔数量有可能不同,价格也有可能不同。为了公平起 见,P 老师决定只买同一种包装的铅笔。
商店不允许将铅笔的包装拆开,因此 P 老师可能需要购买超过 nnn 支铅笔才够给小朋友们发礼物。
现在 P 老师想知道,在商店每种包装的数量都足够的情况下,要买够至少 nnn 支铅笔最少需要花费多少钱。
第一行包含一个正整数 nnn,表示需要的铅笔数量。
接下来三行,每行用 222 个正整数描述一种包装的铅笔:其中第 111 个整数表示这种包装内铅笔的数量,第 222 个整数表示这种包装的价格。
保证所有的 777 个数都是不超过 100001000010000 的正整数。
111 个整数,表示 P 老师最少需要花费的钱。
57
2 2
50 30
30 27
54
9998
128 233
128 2333
128 666
18407
9999
101 1111
1 9999
1111 9999
89991
数据范围
保证所有的 777 个数都是不超过 100001000010000 的正整数。
子任务
子任务会给出部分测试数据的特点。如果你在解决题目中遇到了困难,可以尝试只解决一部分测试数据。
每个测试点的数据规模及特点如下表:
上表中“整倍数”的意义为:若为 KKK,表示对应数据所需要的铅笔数量 nnn —定是每种包装铅笔数量的整倍数(这意味着一定可以不用多买铅笔)。
一共有三种铅笔,并且只能有一种铅笔。这个运算量非常小,所以我们可以直接暴力枚举,枚举每一种包装铅笔需要多少钱,取最小值即可。
怎么计算需要多少包铅笔呢?只要用总共需要的铅笔数除以一包这种包装的铅笔数,如果有余数就再加1,就可以保证能够满足要求,再用需要的包数乘以一包的单价即可求出买这一种铅笔的总价
看样例:
铅笔的三种包装分别是:
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 普及组 T2
在日常生活中,通过年、月、日这三个要素可以表示出一个唯一确定的日期。
牛牛习惯用 888 位数字表示一个日期,其中,前 444 位代表年份,接下来 222 位代表月份,最后 222 位代表日期。显然:一个日期只有一种表示方法,而两个不同的日期的表 示方法不会相同。
牛牛认为,一个日期是回文的,当且仅当表示这个日期的 888 位数字是回文的。现在,牛牛想知道:在他指定的两个日期之间包含这两个日期本身),有多少个真实存在的日期是回文的。
一个 888 位数字是回文的,当且仅当对于所有的 iii(1≤i≤81 \le i \le 81≤i≤8)从左向右数的第 iii 个数字和第 9−i9-i9−i 个数字(即从右向左数的第 iii 个数字)是相同的。
例如:
每一年中都有 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 天。
一个年份是闰年当且仅当它满足下列两种情况其中的一种:
例如:
两行,每行包括一个 888 位数字。
第一行表示牛牛指定的起始日期。
第二行表示牛牛指定的终止日期。
保证 date1\mathit{date}_1date1 和 date2\mathit{date}_2date2 都是真实存在的日期,且年份部分一定为 444 位数字,且首位数字不为 000。
保证 date1\mathit{date}_1date1 一定不晚于 date2\mathit{date}_2date2。
一个整数,表示在 date1\mathit{date}_1date1 和 date2\mathit{date}_2date2 之间,有多少个日期是回文的。
20110101
20111231
1
20000101
20101231
2
【子任务】
对于 60%60 \%60% 的数据,满足 date1=date2\mathit{date}_1 = \mathit{date}_2date1=date2。
枚举两个日期之间的每一天,然后判断有多少个回文数。
用一个数组记录下每个月份的天数,如果是闰年的话,就把2月改成29天,否则改成28天。
如果天数超过了这个月的最大天数,天数归一,月份数加一。
如果月份数超过了12,月份数归一,年数加一。
对于样例 1,符合条件的日期是 201111022011110220111102。
对于样例 2,符合条件的日期是 200110022001100220011002 和 201001022010010220100102。
具体解析看代码
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