清北学堂2017NOIP冬令营入学测试
Posted 日拱一卒 功不唐捐
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了清北学堂2017NOIP冬令营入学测试相关的知识,希望对你有一定的参考价值。
背景
冬令营入学测试题,每三天结算一次成绩。参与享优惠
描述
这是一道有背景的题目,小A也是一个有故事的人。但可惜的是这里纸张太小,小A无法把故事详细地说给大家听。可能小A自己也讲不清楚自己的故事,因为如果讲清了,也就没有这道题目了……
小A的问题是这个样子,它找到了n份不同的工作,第i份工作每个月有ai的工资,每份工作需要小A每天工作8小时,一周工作7天。小A想知道性价比最高(一个月的工资除以总时长)的工作的编号是多少。如果有多份,输出编号最小的就可以了。
输入格式
第一行一个数n,表示有n份工作。
接下来n个数表示ai。
输出格式
输出一个数表示答案。
备注
输入样例
5
3 3 4 5 5
输出样例
4
数据范围
对于100%的数据n<=100,1<=ai<=1000。
因为每份工作时长相等,所以性价比=工资/时长 只看工资。所以本题解法为从前往后找最大的第一个数
#include<cstdio> using namespace std; int n,maxn,ans,x; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&x); if(x>maxn) { maxn=x; ans=i; } } printf("%d",ans); return 0; }
背景
冬令营入学测试
描述
题目描述
小B生活在一个很奇怪的国家里,这个国家的钱的面值只有可能是25,50,100的。小B最近在做社会实践,这次它选择在一个餐厅里干这件事情。但今天发生了一件有趣的事,这件事情是这个样子的,餐厅里大家都在排队买饭,粗心的打饭阿姨忘记要带零钱,并且所有排队打饭的人只带了一张钱。
具体地,第i个人带了一张面额为ai的钱,为了方便起见,我们规定每个人都想买价值25元的饭盒。阿姨显得不知所措。聪明的小B想到了一个方法,让带了25元的先买饭!这样阿姨就有了更多的零钱去找开一些面值较大的钱。
但这样对于一些人来说仍有可能找不开零钱,小B想知道是否存在一种排队方案,能够对所有人找开零钱。如果可行输出“YES”,否则输出“NO”。
输入格式
第一行一个数n,表示有n个想买饭的人。
接下来一行n个数ai,表示第i个人带着的钱的面额。
输出格式
输出“YES”或者“NO”。
备注
输入样例
3
25 50 100
输出样例
NO
数据范围
对于100%的数据n<=100,ai=25或者50或者100。
设25、50、100的人分别为s25,s50,s100
1.如果s25<s50,那么25的不够给50的找钱,输出NO
2.在枚举每个s100过程中(也就是s100>0),有50的先找1张50和1张25,没有50的就找3张25的,中途如果出现手中25不够用的情况,那么输出NO
#include<cstdio> #include<iostream> using namespace std; int n,a,b,c,x;//a,b,c分别代表手持25、50、100的人的总数 int aa,bb,cc;//分别代表打饭阿姨手中25、50、100元的数量 int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&x); if(x==25) a++; else if(x==50) b++; else c++; } aa=a;//25元的不用找钱,打饭阿姨手中多了a张25元 aa-=b;//给每个50找25元钱后剩下几张25 bb=b;//打饭阿姨手中多了b张50元 if(aa<0)//25元的不够找 { cout<<"NO";return 0; } for(int i=1;i<=c;i++)//给每个100的找钱 { if(bb>0)//优先找50元 { bb--; aa--;//找一张50和一张25 if(aa<0)//25的不够找 { cout<<"NO";return 0; } } else if(aa==0)//c还没有枚举完,25元的没了 { cout<<"NO";return 0; } else if(aa>0)//没有50的找3张25 { aa-=3; if(aa<0)//25的不够了 { cout<<"NO";return 0; } } } cout<<"YES";return 0; }
背景
冬令营入学测试
描述
题目描述
小C是一名数学家,由于它自制力比较差,经常通宵研究数学问题。
这次它因为这个数学问题已经两天两夜没有睡觉了,再不研究出来就要出人命了!快帮帮它吧!
这个问题是这样的,有一个数n,将其拆分成若干自然数之和,要求乘积最大!
如果你以为问题仅仅这么简单,那你就太naive了。
由于小C挑战自己的自我修养,它规定分成的自然数两两之间一定不能相等!
它请你输出这个乘积最大是多少,但这个答案太大了,小C并没有兴趣看那么长的数字,它只想知道这个数对1000000007取模后的值是多少。
输入格式
一行一个数表示n
输出格式
一个数表示答案
备注
输入样例
6
输出样例
8
数据范围
对于30%的数据n<=10。
对于50%的数据n<=10000。
对于100%的数据1<=n<=1000000000。
30分暴力
#include<iostream> #include<cstdio> #define mod 1000000007 using namespace std; int n; long long ans; bool v[100001]; void dfs(int now,int remain,long long s)//now当前分出来的数,remain还剩下多少,s乘积 { if(remain==0) { ans=max(s,ans); return ; } for(int i=now+1;i<=remain;i++) { if(!v[i])//不能有重复 { v[i]=true; dfs(i,remain-i,s*i%mod); v[i]=false; } } } int main() { scanf("%d",&n); if(n==1)//1不用拆 { cout<<1; return 0; } dfs(0,n,1); if(ans==n) cout<<ans-1;//2、3、4的结果应该是1、2、3,但dfs结果是2,3,4,因为拆分成了0和本身。dfs时从1开始,所以2,3,4的0乘本身算成了本身。>4的数答案大于本身,所以不用管 else cout<<ans; }
#include<cstdio> #define mod 1000000007 using namespace std; int n,now=2,a[1000000],cnt; long long ans=1; int main() { scanf("%d",&n); if(n<=4) { printf("%d",n); return 0; } while(n>=now) { a[++cnt]=now; n-=now; now++; } if(!n) { for(int i=1;i<=cnt;i++) ans=a[i]%mod*ans%mod; printf("%d",ans); return 0; } int tot=cnt; while(n) { if(!tot) tot=cnt; a[tot--]++; n--; } for(int i=1;i<=cnt;i++) ans=a[i]%mod*ans%mod; printf("%d",ans); return 0; }
背景
冬令营入学测试题
描述
题目描述
小D是一名魔法师,它最喜欢干的事就是对批判记者了。
这次记者招待会上,记者对于小D的数学很好奇。于是小D找了个方法把记者批判了一番。
它对记者抛出了这么一个问题:我有n点能量,写下数字i(1<=i<=9)需要花费a{i}点能量,我用这n点能量最多能写出什么数来?(当然可以不用光n点能量,具体看样例)
记者们一脸懵逼,于是来求助于你。
输入格式
一行10个数,表示n,a1,a2,a3,…,a9。
输出格式
一个数表示答案。
备注
输入样例1
10 2 2 1 2 2 2 2 2 2
输出样例1
3333333333
输入样例2
10 4 11 11 11 11 11 11 11 10
输出样例2
11
数据范围
对于30%的数据n,ai<=10。
对于60%的数据n,ai<=100。
对于100% 的数据1<=n,ai<=1000000,n>=min{ai}。
数的大小首先看位数,其次看最高位,然后是次高位……
位数为总能量/花费能量最小的数
设花费能量最小的数为a,花费能量s,答案至少为s/a位s。如果有多个花费能量最小,取数最大的那个
然后用总能量%s,得出还剩下多少能量值。即还有多少能量可以在让答案位数不变的前提下,变得更大。
然后从9开始倒着枚举,如果剩下的能量足够更新一个,那就让最高位更新,其次是次高位,以此类推。
枚举结束条件:1、枚举到的数<=a 2、当前步骤剩下的能量不能更新任何一个数
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; int n,minn=0x7fffffff,d; int ans[100001]; struct node { int w;//写下p需要w点能量 int p; }a[11]; bool cmp(node x,node y)//从9——1排序 { return x.p>y.p; } int main() { scanf("%d",&n); for(int i=1;i<=9;i++) { scanf("%d",&a[i].w); a[i].p=i; if(a[i].w<=minn)//minn当前耗费最小的能量 { minn=a[i].w; d=a[i].p;//耗费能量minn写下的最大的数 } } int t=n%minn,tt=n/minn;//tt:ans的位数,t:写下tt位d后剩下的能量 sort(a+1,a+10,cmp);//从9——1排序 //sort(a+1,a+10,greater<int>()); int k=1;//当前可更新的最高位 for(int i=1;i<=tt;i++) ans[i]=d;//写下tt位d while(t) { bool ok=false;//ok起的作用:当当前步骤剩下的能量不能更新任何一个数时,退出 for(int i=1;i<=9;i++)//将此处循环改成从9——1,把结构体改成数组a[i]=j代表写下i用j点能量,可以省去前面sort排序 if(a[i].p<=d) break;//a[i].p从大到小排序,如果当前的p小于d,后面的一定小于d,更新会使ans更小。如果p等于d。没有更新必要 else if(a[i].w-minn<=t)//能量可以将d更新成a[i].p { ans[k++]=a[i].p;//先更新,k再++ t=t-(a[i].w-minn); ok=1; break; } if(ok) continue; else break; } for(int i=1;i<=tt;i++) cout<<ans[i]; }
背景
冬令营入学测试
描述
题目描述
小B生活在一个很奇怪的国家里,这个国家的钱的面值只有可能是25,50,100的。小B最近在做社会实践,这次它选择在一个餐厅里干这件事情。但今天发生了一件有趣的事,这件事情是这个样子的,餐厅里大家都在排队买饭,粗心的打饭阿姨忘记要带零钱,并且所有排队打饭的人只带了一张钱。
具体地,第i个人带了一张面额为ai的钱,为了方便起见,我们规定每个人都想买价值25元的饭盒。阿姨显得不知所措。聪明的小B想到了一个方法,让带了25元的先买饭!这样阿姨就有了更多的零钱去找开一些面值较大的钱。
但这样对于一些人来说仍有可能找不开零钱,小B想知道是否存在一种排队方案,能够对所有人找开零钱。
但这个故事是关于小E的。
所以它并不关心能否有这么一种排队方案,它关心的是存在多少这样的排队方案。对于两个持有25元纸币的人,我们认为他们两个人交换位置仍然是同一种排队方案。(也就是说持有同一种纸币的人都可以看作相同的人)
由于答案很大,你只需输出答案对1000000007取模后的结果就可以了。
输入格式
第一行一个数n,表示有n个想买饭的人。
接下来一行n个数ai,表示第i个人带着的钱的面额。
输出格式
输出一个数表示答案。
备注
输入样例
5
25 25 25 50 100
输出样例
5
数据范围
对于30%的数据n<=8。
对于60%的数据n<=20。
对于100%的数据n<=40,ai=25或者50或者100。
60分暴力
#include<cstdio> using namespace std; int n,ga,gb,gc,x,s; void dfs(int now,int aa,int bb,int cc,int a,int b,int c) //now排完几个人,aa,bb,cc当前卖菜阿姨手里的25、50、100元,a,b,c当前还有几个持有25、50、100的人没排队 { if(now==n) { s++;//排完最后一个人了 s%=1000000007; return; } if(a>0)//还有手持25元的 { dfs(now+1,aa+1,bb,cc,a-1,b,c);//排上队,卖菜阿姨手里多了1张25,手持25的人减1个 } if(b>0&&aa>0)//还有手持50元的,同时满足卖菜阿姨能找开钱 { dfs(now+1,aa-1,bb+1,cc,a,b-1,c);//排上队,找钱,减1张25;多一张50,手持50的人减1 } if(c>0)//还有手持100的 { int f=0; if(bb>0&&aa>0) f=1;//卖菜阿姨有50找钱先找50的 else if(aa>=3) f=2;//没有50的找钱找3张25 if(f==1) dfs(now+1,aa-1,bb-1,cc+1,a,b,c-1);//找钱找1张50,1张25,买菜阿姨手里多1张100,100的人减1 else if(f==2) dfs(now+1,aa-3,bb,cc+1,a,b,c-1); //找钱找3张25,减3张25,多1张100,100的人减1 } } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&x); if(x==25) ga++;//分别统计25、50、100的有几张 else if(x==50) gb++; else gc++; } dfs(0,0,0,0,ga,gb,gc); printf("%d",s); }
背景
冬令营入学测试
描述
这个故事是关于小F的,它有一个怎么样的故事呢。
小F是一个田径爱好者,这天它们城市里正在举办马拉松比赛,这个城市可以被看作是n个点m条带权有向边组成的图。马拉松比赛的终点只有一个:点S。
有k个人参加了这场马拉松,小F所在的城市的马拉松与正常的马拉松不一样,每个人的起点都是不相同的,具体地,第i个人从第{ai}个城市出发,并且第i个人的速度是{vi}。每个人当然是会沿着最短路跑到S点,如果一个人跑步的距离是s,速度是v,那么他所花费的时间为s/v。
现在小F想知道,谁是最快到达终点的。若有多个同时到达终点的,就求跑的路最长的,如果有多个同时到达终点且跑的路最长的,就求编号最大的。
小F想知道那个人的编号是多少。
输入格式
第一行3个数字,n,k,m,表示点的个数,跑步的人数,以及路径的条数。
接下来一行m行,每行3个数ai,bi,ci表示有一条从ai到bi长为ci的有向路径。
接下来一行一个数S。
接下来一行k个数,表示每个人的起点xi。
接下来一行k个数,表示每个人的速度vi。
输出格式
输出一个数表示答案。
测试样例1
输入
5 2 10
5 1 9
1 2 81
2 3 30
2 1 46
1 4 45
2 4 48
5 1 93
2 5 61
2 5 21
3 5 45
1
3 5
18 29
输出
2
备注
输入样例
3 2 3
1 2 2
1 3 3
2 3 1
3
2 1
1 3
输出样例
2
数据范围
对于30%的数据n<=5,m<=10。
对于100%的数据n<=300,m<=5000。0<=ci<=100,1<=xi,S<=n,1<=vi<=100,1<=k<=n。
最短路问题,算出每个起点到终点的最短路,然后再算时间
堆优化的dijkstra代码:
#include<cstdio> #include<queue> #include<cstring> #include<iostream> using namespace std; int n,k,m,x,y,z,s; int start[301],speed[301];//start存储起点,speed存储终点 double time[301]; int minn_time;//花费时间最小的人的编号 int head[301]; struct node { int next,to,d; }e[10001]; int DIS[301],cnt; void add(int u,int v,int w)//链表存储 { cnt++; e[cnt].to=v; e[cnt].d=w; e[cnt].next=head[u]; head[u]=cnt; } struct mon { int num,dis; bool operator < (mon k)const //大根堆改成小根堆 { return dis>k.dis; } }; priority_queue<mon>p;//堆优化的spfa int main() { scanf("%d%d%d",&n,&k,&m); for(int i=1;i<=m;i++) { scanf("%d%d%d",&x,&y,&z); add(y,x,z);//因为dijkstra是单源最短路径,所以从每个起点到达固定的终点,可以转化成从固定的起点到达每个终点。所以把x,y交换存储链表 } scanf("%d",&s); memset(DIS,127,sizeof(DIS));//dijkstra初始化 p.push((mon){s,0}); DIS[s]=0; while(!p.empty())//dijikstra模板 { mon now=p.top(); p.pop(); if(DIS[now.num]!=now.dis) continue; for(int i=head[now.num];i;i=e[i].next) { if(DIS[now.num]+e[i].d<DIS[e[i].to]) { DIS[e[i].to]=DIS[now.num]+e[i].d; p.push((mon){e[i].to,DIS[e[i].to]}); } } } for(int i=1;i<=k;i++) scanf("%d",&start[i]); for(int i=1;i<=k;i++) { scanf("%d",&speed[i]); time[i]=(double)DIS[start[i]]/speed[i]; if(minn_time) { double h=time[i]-time[minn_time];//引入h防止精度误差,但本题可以不用考虑,直接用>,==,<也能过 Tyvj2016清北冬令营入学测试洛谷P1080 [NOIP2012提高组D1T2]国王游戏 [2017年5月计划 清北学堂51精英班Day1]