NOIP2016普及组试题题解
1.买铅笔
代码:
#include<bits/stdc++.h> #define ll long long using namespace std; int n,ans=1e9,a,b; int main(){ cin>>n; for(int i=1;i<=3;i++){ cin>>a>>b; ans=min(ans,int(ceil(n*1.0/a)*b)); } cout<<ans; return 0; }
解题思路:
直接枚举每种包装的花费,取最小值即可
2.回文日期
代码:
#include<bits/stdc++.h> #define ll long long using namespace std; int days[13]={0,31,29,31,30,31,30,31,31,30,31,30,31}; int x1,x2,y3,m1,d1,y2,m2,d2,ans=0; int turnday(int m,int d){ return m*100+d+m/10*10000+m%10*100000+d/10*1000000+d%10*10000000; } int main(){ cin>>x1>>x2; for(int i=1;i<=12;i++){ for(int j=1;j<=days[i];j++){ int t=turnday(i,j); if(t>=x1&&t<=x2)ans++; } } cout<<ans; return 0; }
解题思路:
这道题有两种方法,第一种枚举年,反向构造月和日,判断日期是否合法,如果合法且在范围内,答案加一,代码相对繁琐;第二种枚举月和日,反向构造年,这样可以不用判断日期是否合法,只需判断日期是否在范围内即可,代码相对简炼
3.海港
代码:
#include<bits/stdc++.h> #define ll long long using namespace std; const int N = 3e5+39+7; struct node{ int id,tim; node(int id1,int tim1):id(id1),tim(tim1){} }; int cnt=0,vh[N],n,k,t; queue<node>q; int main(){ cin>>n; for(int i=1;i<=n;i++){ cin>>t>>k; for(int j=1,d;j<=k;j++){ cin>>d; q.push(node(d,t)); vh[d]++; if(vh[d]==1)cnt++; } while(q.size()&&t-q.front().tim>=86400){ vh[q.front().id]--; if(!vh[q.front().id])cnt--; q.pop(); } cout<<cnt<<'\n'; } return 0; }
解题思路:
定义一个结构体,用来标记每名游客的国籍与时间,再用一个队列来存储,用一个桶来统计国籍,轮流统计答案即可
4.魔法阵
代码:
#include<bits/stdc++.h> #define ll long long using namespace std; const int N = 2e4+39+7; int vh[N],n,m,a[N*2],sumfre[N],sumend[N],ans[N][5]; int main(){ cin>>n>>m; for(int i=1;i<=m;i++){ cin>>a[i]; vh[a[i]]++; } for(int i=1;i<=n/9;i++){ memset(sumfre,0,sizeof(sumfre)); memset(sumend,0,sizeof(sumend)); for(int j=i*2+1;j<=n;j++)sumfre[j]=sumfre[j-1]+vh[j]*vh[j-i*2]; for(int j=n-i;j>0;j--)sumend[j]=sumend[j+1]+vh[j]*vh[j+i]; for(int j=8*i+2;j<=n-i;j++){ int t=sumfre[j-6*i-1]*vh[j]*vh[j+i]; if(!t)continue; ans[j][3]+=t/vh[j]; ans[j+i][4]+=t/vh[j+i]; } for(int j=n-7*i-1;j>=2*i+1;j--){ int t=sumend[j+6*i+1]*vh[j]*vh[j-2*i]; if(!t)continue; ans[j-2*i][1]+=t/vh[j-i*2]; ans[j][2]+=t/vh[j]; } } for(int i=1;i<=m;i++){ for(int j=1;j<=4;j++){ cout<<ans[a[i]][j]<<' '; } cout<<'\n'; } return 0; }
解题思路:
使用前缀和计算sumfre和sunend,通过推导式子可以直接递推出每次增加多少,最终用ans数组统计即可
注意事项:
1.在求t时,因为题目是求每个物品的出现次数,需要用个数的前缀和乘第一个物品的个数乘第二个物品的个数,分别除以当前物品的个数去求得答案,所以是使用乘法,而不是加法,