其他分享
首页 > 其他分享> > NOIP-2012-J1-真题解析

NOIP-2012-J1-真题解析

作者:互联网

一、选择题

1、A,基础题,考察计算机硬件系统,计算机主机核心包括内存和处理器,缺少内存,将无法启动
2、B、队列是先进先出的线性表
3、A,基础题,考察计算机常识
4、B,基础题,考察进制转换,9A的二进制表示是1001 1010,转8进制是232
5、C,基础题,考察计算机基础,常见的操作系统,C是图形处理软件,是应用软件
6、C,数据结构题,考察二叉树的遍历,C选项的话,无法确定这颗二叉树,前后是矛盾的
7、B,基础题,常见CPU
8、C,算法题,考察冒泡排序算法,冒泡排序是每次比较相邻的两个元素,每一次交换也是交换相邻的两个元素,所以每次交换只减少一个逆序对,枚举序列中的逆序对的个数为10
9、A,基础题,考察计算机历史常识
10、A
11、B,基础题,考察计算机基础,多媒体技术,矢量图的相关概念
12、D,数据结构题,考察栈的出栈入栈序列,代入选项模拟可得D
13、B,基础题,考察计算机网络基础,浏览器
14、C,基础题,考察计算机网络常用应用层协议,电子邮件的服务协议,POP3
15、C,算法题,考察分治算法的基本概念
16、D,基础题,考察计算机硬件知识,CPU地址总线与寻址空间,理论上寻址空间等于总线长度能表示的二进制数据最大范围,32位总线,即2^32=4G
17、C,基础题,考察计算机网络,无线局域网,蓝牙和wifi是常见的无线局域网设备
18、A,基础题,考察递归程序的实现,递归程序的递归执行过程由操作系统分配的栈来实现,如果递归层数过多,会造成栈空间溢出
19、C,基础题,枚举可得1+2+…+9-32-31=36
20、B,基础题,科学常识

二、问题求解

1、每个整点的横纵坐标都是整数,要么是奇数,要么是偶数,只有(奇,偶),(偶,奇),(奇,奇),(偶,偶)4种情况,假设任意两个点的坐标分别是(x1,y1),(x2,y2),则其连线中点坐标是((x1+x2)/2, (y1+y2)/2),除了(奇数+偶数)不能被2整除,其他组合都能被2整除,显然(奇,偶),(偶,奇),(奇,奇),(偶,偶)这4种任意两种坐标的连线中点都不是整点,然后再加上4种中的任意一种坐标,比如(偶,偶),就能找到至少有两个点连线中点是整点,所以答案是5

2、组合数学题,先规定一种类型的5个选手做圆排列,比如5名大陆选手,圆排列有5!/4=24种,每一种排列,都有5个空,可以用来插入5个港澳选手,总共有5!种插入方法,根据乘法原理,总共有24×5!=2880种

三、阅读程序

1、

#include <iostream>
using namespace std;
int a,b,c,d,e,ans;
int main()
{
    cin>>a>>b>>c;
    d=a+b;
    e=b+c;
    ans=d+e;
    cout<<ans<<endl;
    return 0;    
}

编程题,直接代入输入即可得结果为10

2、

#include <iostream>
using namespace std;
int n,i,ans;
int main()
{
    cin>>n;
    ans=0;
    for(i=1;i<=n;i++)
        if(n%i==0) ans++;
    cout<<ans<<endl;
    return 0;    
}

此题求18的约数的个数,依次枚举可得:1,2,3,6,9,18,共6个

3、

#include <iostream>
using namespace std;
int n,i,j,a[100][100];
int solve(int x,int y)
{
    int u,v;
    if(x==n) return a[x][y];
    u=solve(x+1,y);
    v=solve(x+1,y+1);
    if(u>v) return a[x][y]+u;
    else return a[x][y]+v;    
}
int main()
{
    cin>>n;
    for(i=1;i<=n;i++)
        for(j=1;j<=i;j++) cin>>a[i][j];
    cout<<solve(1,1)<<endl;
    return 0;    
}

考察递归函数求解,打表可得结果14
在这里插入图片描述
4、

#include <iostream>
#include <string>
using namespace std;
int n,i,j,ans;
string s;
char get(int i)
{
    if(i<n) return s[i];
    else return s[i-n];    
}
int main()
{
    cin>>s;
    n=s.size();
    ans=0;
    for(i=1;i<=n-1;i++)
    {
        for(j=0;j<=n-1;j++)
            if(get(i+j)<get(ans+j))
            {
                ans=i;
                break;    
            }
            else if(get(i+j)>get(ans+j)) break;
    }
    for(j=0;j<=n-1;j++) cout<<get(ans+j);
    cout<<endl;
    return 0;    
}

首先把get(i)的取值列表列出来,代码中多次调用get(i)函数
在这里插入图片描述
然后打表得到ans=7,然后输出get(ans+j),输出get(7)-get(14),为ACBBADAD
在这里插入图片描述

三、完善程序

1、(坐标统计)输入n个整点在平面上的坐标。对于每个点,可以控制所有位于它左下方的点(即x、y坐标都比它小),它可以控制的点的数目称为“战斗力”。依次输出每个点的战斗力,最后输出战斗力最高的点的编号(如果若干个点的战斗力并列最高,输出其中最大的编号)。

#include <iostream>
using namespace std;
const int SIZE =100;
int x[SIZE],y[SIZE],f[SIZE];
int n,i,j,max_f,ans;
int main()
{
    cin>>n;
    for(i=1;i<=n;i++) cin>>x[i]>>y[i];
    max_f=0;
    for(i=1;i<=n;i++)
    {
        f[i]= [  ①   ];
        for(j=1;j<=n;j++)
        {
            if(x[j]<x[i] && [   ②    ])
            [      ③      ] ;
        }
        if( [     ④       ])
        {
            max_f=f[i];
            [    ⑤    ];    
        }
    }
    for(i=1;i<=n;i++) cout<<f[i]<<endl;
    cout<<ans<<endl;
    return 0;    
}

简单模拟算法,依次统计每个点的战斗力,比较得到最大战斗力的点,若相同,则取最新的,也就是编号最高的
① 0,初始化战斗力都为0
② y[j]<y[i]
③ f[i]++
④ f[i]>=max_f
⑤ ans = i

2、(排列数)输入两个正整数n,m(1<n<20,1<m<n),在1~n中任取m个数,按字典序从小到大输出所有这样的排列。 例如:
输入:3 2
输出:
1 2
1 3
2 1
2 3
3 1
3 2

#include <iostream>
#include <cstring>
using namespace std;
const int SIZE =25;
bool used[SIZE];
int data[SIZE];
int n,m,i,j,k;
bool flag;
int main()
{
    cin>>n>>m;
    memset(used,false,sizeof(used));
    for(i=1;i<=m;i++)
    {
        data[i]=i;
        used[i]=true;    
    }
    flag=true;
    while(flag)
    {
        for(i=1;i<=m-1;i++) cout<<data[i]<<" ";
        cout<<data[m]<<endl;
        flag= [    ①    ] ;
        for(i=m;i>=1;i--)
        {
            [    ②     ];
            for(j=data[i]+1;j<=n;j++)
                if(!used[j])
                {
                    used[j]=true;
                    data[i]=[    ③  ] ;
                    flag=true;
                    break;    
                }
            if(flag)
            {
                for(k=i+1;k<=m;k++)
                    for(j=1;j<= [   ④   ];j++)
                    if(!used[j])
                    {
                        data[k]=j;
                        used[j]=true;
                        break;
                    }
                [     ⑤   ];
            }
        }
    }
    return 0;    
}

目中给的例子太弱了,通常的n个数取m个数排列,要按字典大小顺序输出,如图所示,我们按顺序枚举,每次都需要从右往左找到第一个可以变大的数,比如Ax可以变大为Ay,然后还需要将Ay右边的数重新按大小顺序排序依次枚举出来。
比如n=5,m=4,第一个排列肯定是1,2,3,4,然后从右边开始,第一个4只能变大为5,将5排上,同时将4释放,此刻,5右边没有数了。继续下一轮,第一个能变大的数是3,将3变大为4,同时将3释放,然后3右边还有一个数位,这个数将从剩余的未排上的数里面按从小到大顺序依次选取排列,即3,5,依次类推,直到从右至左遍历完每一个可以变大的数的变大情况。
在这里插入图片描述
while循环的第一部分就是输出当前找到的一个排列
① false,flag是表示当前是否找到一个合法的排列,初值是false,②used[data[i]]=false,从右至左找变大的数替换,首先将待替换的数data[i]释放,然后从data[i]+1至n,选取第一个未选用的数排上来放在data[i]的位置i上,③ j,此刻将flag置为true,表示找到一个新的排列方案,然后如果i右边还有数,则i右边的位置,即i+1到m,用剩余的未选用的数按从小到大顺序依次填上,④ n,⑤ break跳出循环,直接进入下一轮外层while循环的头部,输出上一轮找到的排列方案。

标签:NOIP,真题,int,get,J1,考察,ans,include,data
来源: https://blog.csdn.net/weixin_46966772/article/details/121288389