您的位置:首页 > 其它

NOIP 普及组 复赛 历年 试题

2018-03-13 08:44 519 查看
NOIP 普及组  复赛  历年 试题
NOIP [b]2016 普及组 复赛  试题[/b]
1.买铅笔
https://www.luogu.org/problemnew/show/P1909可提交测评
//P1909 买铅笔
//模拟样例1
//57%2!=0 (57/2+1)*2=58
//57%50!=0 (57/50+1)*30=60
//57%30!=0 (57/30+1)*27=54
//该题是纯模拟的水题,重心在整数的整除,取余,找最值。
//有一个疑虑,int是否会溢出
//试算了一下,10000/1*10000=10^8 即需求10000只笔,每包1只,每只花费10000,故总花费10000/1*10000=10^8,没有溢出
//同时也确定了,min初始化要求min>10^8
//为了拿100分,分析还是挺关键的。
//样例通过,信心满满,但还有些忐忑,提交,哇20个测试点,结果AC 2018-3-13
#include <stdio.h>
int main(){
    int n,a,b,c,min=999999999,i;
    scanf("%d",&n);
    for(i=1;i<=3;i++){
        scanf("%d%d",&a,&b);
        if(n%a!=0)c=(n/a+1)*b;//不能整除
        else c=n/a*b;//能整除
        if(c<min)min=c;
    }
    printf("%d",min);
    return 0;
}


2.回文日期
https://www.luogu.org/problemnew/show/P2010[b]可提交测评[/b]
//P2010 回文日期
//看了日期表示方式,发现正是自己平时常用的表示方式,心定了,题目读得进
//回文,发现平时也有联系
//平年,闰年,平时也有写过
//该题,将功能函数一个个编出,整合是关键
//思考如下,1将两个日期之间所有整数取出,进行回文判定,发现10^8,有超时的风险
//2将两个日期之间的符合条件的日期取出,进行回文判定,2000*365=7*10^5 不会超时
//写得过程中,发现第一年,最后一年需特殊处理
//建议,编写一个功能,测试一次,以防最后抓狂
//该题考点,分析出某个数的各个位置的值,字符串简单处理,整数取余
//样例通过,提交 AC。该题要求 程序代码 编写要比较熟练。 2018-3-13
//该题编写,新手耗时估计1小时
#include <stdio.h>
int y[3],m[3],d[3],a[3],c[]={0,31,0,31,30,31,30,31,31,30,31,30,31},cnt=0;
char s[10];
int date(int a[]){//根据 日期  解析出年 月 日 ,编的过程中,发现设想的难点:前导0没有出现
    int i;
    for(i=0;i<2;i++){
        y[i]=a[i]/10000,a[i]%=10000;
        m[i]=a[i]/100,a[i]%=100;
        d[i]=a[i];
    }
}
int year(int a){//闰年判定,1 闰年 2 非闰年
    if(a%100==0)//整百年处理
        if(a%400==0)return 1;//闰年
        else return 0;
    else if(a%4==0)return 1;
    else return 0;
}
int check(int a){//判断回文,1是,2否
    int len=0,i;
    while(a)s[len++]=a%10+'0',a/=10;//将日期解析成字符串
    for(i=0;i<len/2;i++)
        if(s[i]!=s[len-1-i])return 0;
    return 1;
}
int main(){
    int i,j,k,b;//b新的日期
    scanf("%d%d",&a[0],&a[1]);
    date(a);  
    for(j=1;j<=12;j++){//生成 月 第一年处理
        if(j==2)//2月单独处理
            if(year(y[0]))c[j]=29;//闰年
            else c[j]=28;//非闰年
        for(k=1;k<=c[j];k++){//生成 日
            b=10000*y[0]+j*100+k;
            if(check(b))cnt++;
        }
    }
    for(i=y[0]+1;i<y[1];i++)//生成 月 日
        for(j=1;j<=12;j++){//生成 月
            if(j==2)//2月单独处理
                if(year(i))c[j]=29;//闰年
                else c[j]=28;//非闰年
            for(k=1;k<=c[j];k++){//生成 日
                b=10000*i+j*100+k;
                if(check(b))cnt++;
            }
        }
    if(y[1]!=y[0])//如果没有样例1,这点容易忽略
        for(j=1;j<=12;j++){//生成 月 最后一年处理
            if(j==2)//2月单独处理
                if(year(y[1]))c[j]=29;//闰年
                else c[j]=28;//非闰年
            for(k=1;k<=c[j];k++){//生成 日
                b=10000*y[1]+j*100+k;
                if(check(b))cnt++;
            }
        }
    printf("%d",cnt);
    return 0;
}
 


3.海港
https://www.luogu.org/problemnew/show/P2058[b][b]可提交测评[/b][/b]
//P2058 海港
//看了一眼数据范围,算法复杂度不能是O(n^2),而O(nlogn) 比较合适
//题目配上说明,题意还是很清楚的
//看了题目的测试点,朴素算法,还是能得70分
//仔细想想,该题属于线性序列处理,适用的方法,链表,单调栈,单调队列,树状数组,线段树
//再仔细想想,有单调性的数据,基本可用 单调队列

// 1≤n≤10^5    ∑ki≤3*10^5 困惑比较大,如何处理,内容容易溢出
//P2058 海港
//突然间想到,用前缀和更能优化
//q[100100] sum[1010]提交MLE
//q[10010] sum[1010]提交 70分,测试点7-11,13 RE 很满意 2018-3-30 21:23
//以下代码为70分代码
//从70分跨越到100分,估计要改算法,想不下去了,考虑看看题解
//https://blog.csdn.net/c20190102/article/details/53761648此文代码写得不错
//看了看,算法的区别在于,应选人为研究对象,而不是选船为研究对象,不过,一开始没想到,之后也确实想不到
//从上述角度,还是说明,算法确实不同
//样例通过,提交AC 2018-3-31 17:14
#include <stdio.h>
#include <string.h>
struct node{
    int time;
    int x;
}q[300100];//人
int h,t,vis[100100];
int main(){
    int n,time,k,i,x,cnt=0;//此处写成 int n,time,k,i,x,cnt; 忘记将cnt初始化
    memset(vis,0,sizeof(vis));
    scanf("%d",&n);
    h=t=1;
    while(n--){
        scanf("%d%d",&time,&k);
        while(h<t&&q[h].time+86400<=time){
            vis[q[h].x]--;
            if(!vis[q[h].x])cnt--;//像不像 欧拉图 的计算
            h++;
        }
        for(i=1;i<=k;i++){
            scanf("%d",&x),q[t].time=time,q[t].x=x,t++;
            if(!vis[x])cnt++;
            vis[x]++;//有点像 欧拉图 的计算
        }
        printf("%d\n",cnt);
    }
    return 0;
}


//以下代码为70分代码,仅供参考
//P2058 海港
//突然间想到,用前缀和更能优化
//q[100100] sum[1010]提交MLE
//q[10010] sum[1010]提交 70分,测试点7-11,13 RE 很满意 2018-3-30 21:23
//以下代码为70分代码
#include <stdio.h>
#include <string.h>
struct node{
    int time;
    int k;
    int sum[1010];//此处开多少,没有把握
}q[10010];
int h,t;
int main(){
    int n,time,k,i,j,cnt;
    memset(q,0,sizeof(q));
    scanf("%d",&n);
    h=t=1;
    while(n--){
        scanf("%d%d",&time,&k),q[t].k=k,q[t].time=time;
        while(h<t&&q[h].time+86400<=time)h++;//此处写成 while(h<t&&q[h].time+86400>=time)h++;查了好久
        for(i=1;i<=k;i++)scanf("%d",&j),q[t].sum[j]=1;
        for(j=1;j<=1000;j++)q[t].sum[j]+=q[t-1].sum[j];
        t++;
        cnt=0;
        for(i=1;i<=1000;i++)
            if(q[t-1].sum[i]-q[h-1].sum[i])cnt++;
        printf("%d\n",cnt);
    }
    return 0;
}


4.魔法阵
https://www.luogu.org/problemnew/show/P2119[b][b][b]可提交测评[/b][/b][/b]
[b][b][b]//P2119 魔法阵
//https://blog.csdn.net/c20181503csy/article/details/53319238此文写得比较粗
//https://blog.csdn.net/c20182030/article/details/53289443此文写得比较细
//可两篇文章,结合起来看
//需用到桶排序+排列组合知识
//样例通过,提交AC 2018-4-5 12:54
#include <stdio.h>
#include <string.h>
#define maxn 15100
#define maxm 40100
#define LL long long
LL w[maxn],h[maxm],a[maxn],b[maxn],c[maxn],d[maxn];//h[i] i存储物品序号 h[i]为物品魔法值 w[i]中i为魔法值,w[i]表示魔法值为i的物品个数 a[i]中i为魔法值 a[i]为该魔法值对应魔法阵的使用次数
LL n,m;
LL min(LL a,LL b){
    return a<b?a:b;
}
LL max(LL a,LL b){
    return a>b?a:b;
}
int main(){
    LL i,j,x,y,min_n=16000,max_n=-1;
    memset(w,0,sizeof(w));
    scanf("%lld%lld",&n,&m);
    for(i=1;i<=m;i++)scanf("%lld",&h[i]),w[h[i]]++,min_n=min(min_n,h[i]),max_n=max(max_n,h[i]);//此处做了优化,找出魔法值的最小值min_n,最大值max_n,之后整个程序运行时间缩短了一半
    for(i=1;9*i<max_n-min_n;i++){//间距最小从1开始,自加,max_n-min_n是魔法值之间的最大间距
        x=9*i+1,y=0;//x=9*i+1是a d之间的最小间距
        for(j=min_n+9*i+1;j<=max_n;j++){//从D点开始枚举最小间距是9*i+1  因A B 在C D的左侧 顺序枚举
            y+=w[j-x]*w[j-x+2*i];//计算a*b的组合 j-x 表示A物体的魔法值 j-x+2*i表示 B物体的魔法值 . 之前的a b影响之后的c d故,之前的a b组合数需累加
            d[j]+=y*w[j-i];//d=a*b*c j-i表示 C物体的魔法值
            c[j-i]+=y*w[j];//c=a*b*d j表示 D物体的魔法值
        }
        x=8*i+1,y=0;//注意:因C D在A B的右侧,循环需逆序 枚举,顺序不行
        for(j=max_n-(9*i+1);j>=min_n;j--){//从A点开始枚举 顺序枚举不行,即for(j=min_n;j<=max_n-(9*i+1);j++)是不可以的
            y+=w[j+x]*w[j+x+i];//计算c*d的组合 j+x表示C物体的魔法值,j+x+i表示D物体的魔法值. 之前的c d影响之后的a b故,之前的c d组合数需累加
            a[j]+=y*w[j+2*i];//j+2*i表示B物体的魔法值,
            b[j+2*i]+=y*w[j];//j表示A物体的魔法值
        }
    }
    for(i=1;i<=m;i++)printf("%lld %lld %lld %lld\n",a[h[i]],b[h[i]],c[h[i]],d[h[i]]);//此处写成 for(i=1;i<=m;i++)printf("%d %d %d %d\n",a[h[i]],b[h[i]],c[h[i]],d[h[i]]);查了会
    return 0;
}

[/b][/b][/b]
[b]//P2119 魔法阵
//试了 普及组的 深浅
//发现 没有超出 范围 的 未掌握的 知识点
//该题,若是比赛时,也没想着拿满分,只要有分拿,即可
//先快排
//开一个数组,存储对应序号的顺序,如q[i]=j,表示 自小到大 排在 第i位 元素 对应的 序号是 j 该思想意味着水平的大幅提升
//https://blog.csdn.net/ssl_zzy/article/details/53308193比较喜欢该文的写法
//测试了一下,发现,样例1,遗漏 物品1,3,7,6,其魔法值分别为1,7,26,29;
//经测试,发现7-1=6 26-7=19 19/3=6
//故,需要将除改成 乘
//即xb-xa<(xc-xb)/3 改成 3*(xb-xa)<(xc-xb)
//预测 四重 循环,能到测试点 7
//提交,发现,结果,不是WA ,就是TLE
//重新读题,发现"保证标准输出中的每个数都不会超过10^9","每行相邻的两个数之间用恰好一个空格隔开"
//果断改成long long并按题目要求,改写输出格式
//提交,发现,结果,不是WA ,就是TLE
//if(x[j]-x[i]==2*(x[p]-x[k])&&3*(x[j]-x[i])<x[k]-x[j]&&x[i]<x[j]&&x[j]<x[k]&&x[k]<x[p])//此处写成 if(x[j]-x[i]==2*(x[p]-x[k])&&3*(x[j]-x[i])<x[k]-x[j])确实忘了==情况的判定
//提交,测试点4-11 TLE 测试点12 TLE 得分55 2018-4-2 18:45
#include <stdio.h>
#include <string.h>
int q[40100],n,m;
long long x[40100],cnt[40100][5];
void quicksort(int left,int right){//自小到大
    int i=left,j=right,t1;
    long long mid=x[(left+right)/2],t2;
    while(i<=j){
        while(x[i]<mid)i++;
        while(x[j]>mid)j--;
        if(i<=j){
            t2=x[i],x[i]=x[j],x[j]=t2;
            t1=q[i],q[i]=q[j],q[j]=t1;
            i++,j--;
        }
    }
    if(left<j)quicksort(left,j);
    if(i<right)quicksort(i,right);
}
int main(){
    int i,j,k,p;
    memset(cnt,0,sizeof(cnt));
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++)scanf("%lld",&x[i]),q[i]=i;
    quicksort(1,m);
    for(i=1;i<=m;i++)
        for(j=i+1;j<=m;j++)
            for(k=j+1;k<=m;k++)
                for(p=k+1;p<=m;p++)
                    if(x[j]-x[i]==2*(x[p]-x[k])&&3*(x[j]-x[i])<x[k]-x[j]&&x[i]<x[j]&&x[j]<x[k]&&x[k]<x[p])//此处写成 if(x[j]-x[i]==2*(x[p]-x[k])&&3*(x[j]-x[i])<x[k]-x[j])确实忘了==情况的判定
                        cnt[q[i]][1]++,cnt[q[j]][2]++,cnt[q[k]][3]++,cnt[q[p]][4]++;
    for(i=1;i<=m;i++){
        for(j=1;j<=4;j++)
            if(j==1)printf("%lld",cnt[i][j]);
            else printf(" %lld",cnt[i][j]);
        printf("\n");
    }
    return 0;
}

NOIP [b]2015 普及组 复赛  试题[/b]
[/b]
1.金币
https://www.luogu.org/problemnew/show/P2669可提交测评
//P2669 金币
//因k<=10000;每天收到10000枚,10000天,共计10000*10000=10^8,很明显int够用了
//并且,该题 纯模拟 即可
//循环次数,1+2+3+...+n=(1+n)*n/2 n^2=10000极限n=100次
//样例通过,
//自个编了几组测试样例,
//样例
//1
//1
//样例
//2
//3
//样例
//3
//5
//样例
//4
//8
//样例
//5
//11
//样例
//7
//18
//均顺利通过,放心了,提交AC 2018-4-5 15:00
#include <stdio.h>
int main(){
    int k,i=1,sum=0,cnt=0;
    scanf("%d",&k);
    while(1){
        sum+=i*i;
        cnt+=i;//天数
        if(cnt>k)break;
        i++;
    }
    cnt-=i,sum-=i*i;//回退,保证cnt<=k//多出天数的处理
    sum+=(k-cnt)*i;//之后的第i天处理,天数是k-cnt,金币,每天是i
    printf("%d",sum);
}


2.扫雷游戏
https://www.luogu.org/problemnew/show/P2670可提交测评
//P2670 扫雷游戏
//题目,还没有到广度优先遍历的难度
//具体就是枚举任意一点,若该点是地雷,就不做任何事,若是非地雷就枚举8个方向,统计地雷数
//有广度优先遍历的基础,该题就是一道水题。
//样例通过,提交AC 2018-4-5 15:33
//采用字符串方式读取雷区
#include <stdio.h>
char s[110][110];
int n,m,d[][2]={{-1,0},{1,0},{0,-1},{0,1},{-1,-1},{-1,1},{1,-1},{1,1}};//上 下 左 右 左上 右上 左下 右下
int main(){
    int i,j,k,nr,nc,cnt;
    scanf("%d%d",&n,&m);
    for(i=0;i<n;i++)scanf("%s",s[i]);
    for(i=0;i<n;i++){//基本思路,边处理 边打印
        for(j=0;j<m;j++)
            if(s[i][j]=='*')printf("*");//地雷
            else {
                cnt=0;
                for(k=0;k<8;k++){
                    nr=i+d[k][0],nc=j+d[k][1];//八个方向枚举
                    if(0<=nr&&nr<n&&0<=nc&&nc<m&&s[nr][nc]=='*')
                        cnt++;
                }
                printf("%d",cnt);
            }
        printf("\n");
    }
    return 0;
}


3.求和
https://www.luogu.org/problemnew/show/P2671可提交测评
//P2671 求和
//看了一样10007基本可以确定是质数,简单编码做了检验,发现确实是质数
//检验代码如下
//看了数据范围,O(n^2)超时,要么O(n),要么O(nlogn)
//有了NOIP 2016 普及组 魔法阵 的基础后 该题就容易了
//看看int是否会溢出,极限情况取一个1 n/2 n 三元组 (1+100000)*(100000+100000)=2*10^10很明显int溢出,采用long long
//样例通过,看了看数据范围,发现仅能通过前四组测试数据,得分40
//提交,确实如此,测试点2,6-10RE,得分40
//只按序号处理,40分,只按颜色处理,猜测60分 100000*20=2*10^6
//以下为40分代码
#include <stdio.h>
#define maxn 100100
#define LL long long
#define mod 10007
LL n,m,number[maxn],color[maxn];
int main(){
    LL i,ans=0,x,z;
    scanf("%lld%lld",&n,&m);
    for(i=1;i<=n;i++)scanf("%lld",&number[i]);
    for(i=1;i<=n;i++)scanf("%lld",&color[i]);
    for(i=1;2*i<=n-1;i++)//i=y-x=z-y i是间距
        for(x=n-2*i;x>=1;x--){
            z=x+2*i;
            if(color[x]==color[z]){
                ans=(ans+(x+z)*(number[x]+number[z]))%10007;
            }
        }
    printf("%lld",ans);
    return 0;
}


//P2671 求和
//看了一样10007基本可以确定是质数,简单编码做了检验,发现确实是质数
//检验代码如下
#include <stdio.h>
int main(){
    int i;
    for(i=2;i*i<=10007;i++)
        if(10007%i==0)printf("i=%d",i);
    return 0;
}


4.推销员
https://www.luogu.org/problemnew/show/P2672可提交测评
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: