NOIP 2018 提高组初赛试题 题目+答案+简要解析

一、单项选择题(共 10  题,每题 2  分,共计 20  分; 每题有且仅有一个正确选项)
 
 
 
1. 下列四个不同进制的数中,与其它三项数值上不相等的是( )。
A. (269) 16
B. (617) 10
C. (1151) 8
D. (1001101011) 2
 
答案:D
 
解析:进制转换,把所有的选项都转换成相同的进制即可。 至于转成几进制,看个人喜好
 
2. 下列属于解释执行的程序设计语言是( )。
A. C
B. C++
C. Pascal
D. Python
 
答案:D
 
解析:可以理解为:需要编译的就是非解释性语言。本人印象最深的解释型语言是Java,有什么错写完一句就报出来。
C,C++,pascal都是需要编译的语言,就是非解释性语言
而python是交互式的,也是解释性语言
 
 
3. 中国计算机学会于( )年创办全国青少年计算机程序设计竞赛。
A. 1983
B. 1984
C. 1985
D. 1986
 
答案:B
 
解析:???我也不知道
 
4. 设根节点深度为 0,一棵深度为 h 的满 k(k>1)叉树,即除最后一层无任何
子节点外,每一层上的所有结点都有 k 个子结点的树,共有( )个结点。 
A. 

(k^{h+1}-1)/(k-1)(kh+11)/(k1)

B. 

k^{h-1}kh1


C. 

k^hkh


D.

(k^{h-1})/(k-1)(kh1)/(k1)

 
答案:A
 
解析:等比数列求和。我是蒟蒻不会?多画两棵树自己试(逃
 
 
5. 设某算法的时间复杂度函数的递推方程是 T(n) = T(n - 1) + n(n 为正整数)
及 T(0) = 1,则该算法的时间复杂度为( )。
 
  A. O(log n)
 B. O(n log n)
 C. O(n)
 D. O(n^2)
 
答案:D
 
解析:
NOIP初赛中的时间复杂度分析题就是授人以鱼,考人以鱽鱾鲀鱿鲃鲂鲉鲌鲄鲆鲅鲇鲏鲊鲋鲐鲈鲍鲎鲝鲘鲙鲗鲓鲖鲞鲛鲒鲚鲜鲟鲔鲕鲑鲧鲬鲪鲫鲩鲣鲨鲡鲢鲤鲠鲥鲦鲺鲯鲹鲴鲶鲳鲮鲭鲵鲲鲰鲱鲻鲷鲸鳋鳊鳁鳀鲾鲼鳈鳉鳃鳄鲿鳇鳂鳆鳅鲽鳌鳒鳎鳏鳑鳐鳍鳘鳛鳕鳓鳙鳗鳚鳔鳖鳜鳟鳞鳝鳡鳠鳢鳣鳤。
 引自洛谷日报(逃
但我觉的可能就是求和……要把式子展开,变成
 1+1+2+3+...+(n-1)+n=1+n*(n+1)/2然后忽略常数复杂度总和变成n^2
 
  A. a d * b c * -
 B. - * a d * b c
 C. a * d - b * c
 D. - * * a d b c
答案:B
 
解析:先建一棵表达式树,先序遍历就是前缀表达式
 
 
7. 在一条长度为 1 的线段上随机取两个点,则以这两个点为端点的线段的期望
长度是( )。
A. 1 / 2
B. 1 / 3
C. 2 / 3
D. 3 / 5
 
答案:B
 
解析:全靠猜
我们设这个区间[l,r]l=0,因题目0
0-r长度的期望为1/2,显然l-r的期望会比0-r小……所以就是1/3了(
 
 
8. 关于 Catalan 数 Cn = (2n)! / (n + 1)! / n!,下列说法中错误的是( )。
A. Cn 表示有 n + 1 个结点的不同形态的二叉树的个数。
B. Cn 表示含 n 对括号的合法括号序列的个数。
C. Cn 表示长度为 n 的入栈序列对应的合法出栈序列个数。
D. Cn 表示通过连接顶点而将 n + 2 边的凸多边形分成三角形的方法个数。
 
答案:A
 
解析:基本知识?反正我不会QwQ
找个数带进去就行
 
 
9. 假设一台抽奖机中有红、蓝两色的球,任意时刻按下抽奖按钮,都会等概率
获得红球或蓝球之一。有足够多的人每人都用这台抽奖机抽奖,假如他们的
策略均为:抽中蓝球则继续抽球,抽中红球则停止。最后每个人都把自己获
得的所有球放到一个大箱子里,最终大箱子里的红球与蓝球的比例接近于
( )。
A. 1 : 2
B. 2 : 1
C. 1 : 3
D. 1 : 1
 
答案:D
 
解析:算出每一轮拿到红球的期望为1,拿到蓝球必定1个,所以比例会接近1:1
 
 
10. 为了统计一个非负整数的二进制形式中 1 的个数,代码如下:
int CountBit(int x)
{
  int ret = 0;
  while (x)
  {
      ret++;
      ________;
  }
  return ret;
}
则空格内要填入的语句是( )。
A. x >>= 1
B. x &= x - 1
C. x |= x >> 1
D. x <<= 1
 
答案:B
 
解析:排除法+手算
 
 
二 、 不定 项选择题(共 5  题,每题 2  分,共计 10  分 ;每题有一个或多个正确选
项,多选或少选均不得分 )
 
 
1. NOIP 初赛中,选手可以带入考场的有( )。
A. 笔
B. 橡皮
C. 手机(关机)
D. 草稿纸
 
答案:AB
 
解析:凭感觉
 
 
2. 2-3 树是一种特殊的树,它满足两个条件:
(1)每个内部结点有两个或三个子结点;
(2)所有的叶结点到根的路径长度相同。
如果一棵 2-3 树有 10 个叶结点,那么它可能有( )个非叶结点。
A. 5
B. 6
C. 7
D. 8
 
答案:CD
 
解析:自己构造树
 
 
3. 下列关于最短路算法的说法正确的有( )。
A. 当图中不存在负权回路但是存在负权边时,Dijkstra 算法不一定能求出源
点到所有点的最短路。
B. 当图中不存在负权边时,调用多次 Dijkstra 算法能求出每对顶点间最短路
径。
C. 图中存在负权回路时,调用一次 Dijkstra 算法也一定能求出源点到所有点
的最短路。
D. 当图中不存在负权边时,调用一次 Dijkstra 算法不能用于每对顶点间最短
路计算。
 
答案:ABD
 
解析:dijstra算法不适用于负权图,而且它用于求单点到其他点的最短路
 
 
4. 下列说法中,是树的性质的有( )。
A. 无环
B. 任意两个结点之间有且只有一条简单路径
C. 有且只有一个简单环
D. 边的数目恰是顶点数目减 1
 
答案:ABD
 
解析:树的基本知识
 
 
5. 下列关于图灵奖的说法中,正确的有( )。
A. 图灵奖是由电气和电子工程师协会(IEEE)设立的。
B. 目前获得该奖项的华人学者只有姚期智教授一人。
C. 其名称取自计算机科学的先驱、英国科学家艾伦·麦席森·图灵。
D. 它是计算机界最负盛名、最崇高的一个奖项,有“计算机界的诺贝尔奖”
之称。
 
答案:BCD
 
解析:你觉得A能对吗
--------------------- 

二、问题求解(每题5分,共10分)

 

1. 甲乙丙丁四人在考虑周末要不要外出郊游。

已知①如果周末下雨,并且乙不去,则甲一定不去;②如果乙去,则丁一定去;③如果丙去,则丁一定不去;④如果丁不去,而且甲不去,则丙一定不去。如果周末丙去了,则甲_____(去了/没去)(1分),乙_____(去了/没去)(1分),丁_____(去了/没去)(1分),周末_____(下雨/没下雨)(2分)。

 

答案:去了,没去,没去,没下雨

 解析:送分题,根据条件判断即可

 

2. 方程 a*b =(a or b)  *  (a andb),在a, b都取[0、31]中的整数时,共有______组解。(*表示乘法;or表示按位或运算;and表示按位与运算)

 

解析:使得a|b==max(a,b),a&b==min(a,b)

显然,a,b存在子集关系时上面的式子才能成立。

例如:1011|1001=1011=max(1011,1001)

可以自己举例验证QwQ

我们设b是a的子集,枚举a在二进制下有多少个1,即从5里面取i个1,i从0到5。每个i都是一个C(5, i)

接着枚举子集,有2^i中方案(每一个1都看看放还是不放)

C(5,0)*2^0 +C(5,1)*2^1+…+C(5,5)*2^5

= 1*1 + 5*2 + 10*4 + 10*8 + 5*16 + 1*32

= 243

最后,由于我们限制了a>b,所以答案要*2

但是a==b的情况会被多算,所以答案减去32

最后结果:2*243 – 32 = 452

 

四、阅读程序写结果(共 4 题,每题8 分,共计 32 分)

1.

#include

int main() {

int x;

scanf("%d", &x);

int res = 0;

for (int i = 0; i < x; ++i) {

    if (i * i % x == 1) {

        ++res;

    }

}

printf("%d", res);

return 0;

}

输入:15

输出:4

解析:简单模拟

2.

#include

int n, d[100];

bool v[100];

int main() {

scanf("%d", &n);

for (int i = 0; i < n; ++i) {

    scanf("%d", d + i);

    v[i] = false;

}

int cnt = 0;

for (int i = 0; i < n; ++i) {

    if (!v[i]) {

        for (int j = i; !v[j]; j = d[j]) {

            v[j] = true;

        }

        ++cnt;

    }

}

printf("%d\n", cnt);

return 0;

}

输入:10 7 1 4 3 2 5 9 8 0 6

输出:6

 

解析:继续模拟(逃

 

3.

#include

using namespace std;

string s;

long longmagic(int l, int r) {

long long ans = 0;

for (int i = l; i <= r; ++i) {

    ans = ans * 4 + s[i] - 'a' + 1;

}

return ans;

}

int main() {

cin >> s;

int len = s.length();

int ans = 0;

for (int l1 = 0; l1 < len; ++l1) {

    for (int r1 = l1; r1 < len; ++r1) {

        bool bo = true;

        for (int l2 = 0; l2 < len; ++l2) {

            for (int r2 = l2; r2 < len; ++r2) {

                if (magic(l1, r1) == magic(l2, r2)&& (l1 != l2 || r1 != r2)) {

                    bo = false;

                }

            }

        }

        if (bo) {

            ans += 1;

        }

    }

}

cout << ans << endl;

return 0;

}

输入:abacaba

输出:16

 

解析:magic(l,r)是l-r的hash,枚举两个子串,答案就是不重复出现的子串个数,手动枚举。

 

4.

#include

using namespace std;

const int N =110;

bool isUse[N];

int n, t;

int a[N], b[N];

bool isSmall() {

for (int i = 1; i <= n; ++i)

    if (a[i] != b[i]) return a[i] < b[i];

return false;

}

boolgetPermutation(int pos) {

if (pos > n) {

    return isSmall();

}

for (int i = 1; i <= n; ++i) {

    if (!isUse[i]) {

        b[pos] = i; isUse[i] = true;

        if (getPermutation(pos + 1)) {

            return true;

        }

        isUse[i] = false;

    }

}

return false;

}

void getNext() {

for (int i = 1; i <= n; ++i) {

    isUse[i] = false;

}

getPermutation(1);

for (int i = 1; i <= n; ++i) {

    a[i] = b[i];

}

}

int main() {

scanf("%d%d", &n, &t);

for (int i = 1; i <= n; ++i) {

    scanf("%d", &a[i]);

}

for (int i = 1; i <= t; ++i) {

    getNext();

}

for (int i = 1; i <= n; ++i) {

    printf("%d", a[i]);

    if (i == n) putchar('\n'); else putchar('');

}

return 0;

}

输入1:6 10 1 6 4 5 32

输出 1:2 1 3 5 6 4 (3 分)

输入2:6 200 1 5 3 4 26

输出 2:3 2 5 6 1 4 (5 分)

 

解析:这一大堆函数就是求这个排列的下一个……手算即可(当然如果你觉得200算不出来可以使用康托展开)



五、完善程序(共 2 题,每题 14 分,共计 28 分)

 

1. 对于一个1到n的排列p(即1到n中每一个数在p中出现了恰好一次),令qi为第i个位置之后第一个比pi值更大的位置,如果不存在这样的位置,则qi =n+1。

举例来说,如果n=5且p为1 5 4 2 3,则q为2 6 6 5 6。

下列程序读入了排列p,使用双向链表求解了答案。试补全程序。(第二空2分,其余3分)

数据范围 1 ≤ n ≤ 105。

 

#include

using namespace std;

const int N =100010;

int n;

int L[N], R[N],a[N];

int main() {

cin >> n;

for (int i = 1; i <= n; ++i) {

    int x;

    cin >> x;

    a[x] = i ;

}

for (int i = 1; i <= n; ++i) {

    R[i]= i + 1;

    L[i] = i - 1;

}

for (int i = 1; i <= n; ++i) {

    L[ R[a[i]]] = L[a[i]];

    R[L[a[i]]] = R[a[i] ];

}

for (int i = 1; i <= n; ++i) {

    cout << R[i]<< " ";

}

cout << endl;

return 0;

}

解析:

纯靠猜。1空发现没有直接读入,肯定有鬼……瞎猜一种方法就行

2空仿写下句……

3,4空互相仿写……

5空我们肯定要求这个数右边比他大的数的位置,所以输出R

当然我蒻看不懂,有心情研究的julao们可以再去找找……

 

2. 一只小猪要买 N 件物品(N 不超过 1000)。

它要买的所有物品在两家商店里都有卖。第 i 件物品在第一家商店的价格是 a[i],在第二家商店的价格是 b[i],两个价格都不小于 0 且不超过 10000。如果在第一家商店买的物品的总额不少于 50000,那么在第一家店买的物品都可以打 95 折(价格变为原来的 0.95 倍)。

求小猪买齐所有物品所需最少的总额。

输入:第一行一个数 N。接下来 N 行,每行两个数。第 i 行的两个数分别代表 a[i],b[i]。

输出:输出一行一个数,表示最少需要的总额,保留两位小数。

试补全程序。(第一空 2 分,其余 3 分)

 

#include 

#include 

using namespace std;

 

const int Inf = 1000000000;

const int threshold = 50000;

const int maxn = 1000;

 

int n, a[maxn], b[maxn];

bool put_a[maxn];

int total_a,total_b;

double ans;

intf[threshold];

 

int main() {

  //第一部分

  scanf("%d",&n);

  total_a= total_b = 0;

  for(int i = 0; i < n; ++i) {

    scanf("%d%d",a + i, b + i);

    if(a[i] <= b[i]) total_a += a[i];

    elsetotal_b += b[i];

  }

  ans =total_a + total_b;

  total_a= total_b = 0;

 


  for(int i = 0; i < n; ++i) {

    if( (1) ) {

      put_a[i]= true;

      total_a+= a[i];

    }else {

      put_a[i]= false;

      total_b+= b[i];

    }

  }

  if ((2) ) {

    printf("%.2f",total_a * 0.95 + total_b);

    return0;

  }

 
//第二部分

  f[0]= 0;

  for(int i = 1; i < threshold; ++i)

    f[i]= Inf;

  inttotal_b_prefix = 0;

  for(int i = 0; i < n; ++i)

    if (!put_a[i]) {

      total_b_prefix += b[i];

      for (int j = threshold - 1; j >= 0; --j) {

 if ( (3) >= threshold && f[j] != Inf)

   ans = min(ans, (total_a + j +a[i]) * 0.95+ (4) );

        f[j] = min(f[j] + b[i], j >= a[i] ? (5) :Inf);

      }

    }

  printf("%.2f",ans);

  return0;

}

 

解析:

代码分为两个部分。

第一部分是一个贪心,我们假设满足了优惠条件,按照折后价格进行贪心,如果结果满足了优惠条件就直接输出,此时如果放弃某些b商品来买a不会再有更优策略

第二部分是一个dp……策略就是把原先买了b的东西买a以获得折扣

f[i,j]表示前i个物品,在额外在A店花了j元的情况下,购买B店物品花费的最小值。i呢?想想你的01背包是怎么优化的。

3空是一个转移判断条件,tot_a+j+a[i]是在a店话费的总钱数(本来花的钱+前面改了的钱+当前物品价格)

4空表示在b商店买的物品总价, total_b  + f[j] -  total_b_prefix

5空更新,看看这个商品在a商店买还是b商店买

 

太毒瘤了QwQ

你可能感兴趣的:(NOIP 2018 提高组初赛试题 题目+答案+简要解析)