noip2018提高组初赛试题
阅读原文时间:2023年07月08日阅读:3

一、单项选择题(共 10 题,每题 2 分,共计 20 分; 每题有且仅有一个正确选项)

\2. 下列属于解释执行的程序设计语言是( )。

A. C

B. C++

C. Pascal

D. Python

答案:D

解析:编译语言 :C/C++、Pascal/Object Pascal(Delphi)

​ 解释性语言 :JavaScript、VBScript、Perl、Python、Ruby、MATLAB

 区别就是编译语言要先翻译成中间代码,每执行一次都要翻译一次

\4. 设根节点深度为 0,一棵深度为 h 的满 k(k>1)叉树,即除最后一层无任何

子节点外,每一层上的所有结点都有 k 个子结点的树,共有( )个结点。

A.

\[(k^{h+1}-1)/(k-1)
\]

B.

\[k^{h-1}
\]

C.

\[k^h
\]

D.

\[(k^{h-1})/(k-1)
\]

答案:A

解析:做得时候是自己画了几棵树带进去,结果选了D

知乎上一位大佬的解释

对于满 k 叉树,第 0 层有 1 个节点,第 1 层有 k 个节点,第 2 层有 k2 个节点,第 3 层有 k3 个节点,……,第 h 层有 kh 个节点。

运用等比数列求和公式,一共有 ![截屏2022-08-14 16.18.27](/Users/hongshengkai/Library/Application Support/typora-user-images/截屏2022-08-14 16.18.27.png)

个节点。

\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

解析:简单推了一下,例如

T(1) = T(0) + 1,T(2) = T(1) + 2,T(3) = T(2) + 3; 以为是O(N), 但是每一个T(i)都要重新算一遍 为什么不加个记忆化捏 T_T

\7. 在一条长度为 1 的线段上随机取两个点,则以这两个点为端点的线段的期望

长度是( )。

A. 1 / 2

B. 1 / 3

C. 2 / 3

D. 3 / 5

答案:B

解析:百度:可以通过几何概型+体积来求。建立一个三维坐标系Oxyz,x轴代表点A位置,y轴代表点B位置,z轴代表线段长度期望,那么长度的期望就是两个四面体拼起来的图形,顶点为(0,0,0),(1,0,0),(0,1,0),(0,0,1)和(1,1,0),(1,0,0),(0,1,0),(1,1,1)。这个几何体的体积为1/3(锥体体积为底面积乘以高再除以3),由于底面积为1,所以高度平均为1/3,即长度期望为1/3。

\8. 关于 Catalan 数 Cn = (2n)! / (n + 1)! / n!,下列说法中错误的是( )。

A. Cn 表示有 n + 1 个结点的不同形态的二叉树的个数。

B. Cn 表示含 n 对括号的合法括号序列的个数。

C. Cn 表示长度为 n 的入栈序列对应的合法出栈序列个数。

D. Cn 表示通过连接顶点而将 n + 2 边的凸多边形分成三角形的方法个数。

答案:A

解析:卡特兰数是……,满足递推式

\[h(n)= h(0)*h(n-1)+h(1)*h(n-2) + … + h(n-1)*h(0) (n≥2)
\]

之后出一篇博客来理解好了……

二 、 不定 项选择题(共 5 题,每题 2 分,共计 10 分 ;每题有一个或多个正确选

项,多选或少选均不得分 )

\1.官方 | CCF NOIP竞赛须知 (sohu.com)

\3. 下列关于最短路算法的说法正确的有( )。

A. 当图中不存在负权回路但是存在负权边时,Dijkstra 算法不一定能求出源点到所有点的最短路。

B. 当图中不存在负权边时,调用多次 Dijkstra 算法能求出每对顶点间最短路径。

C. 图中存在负权回路时,调用一次 Dijkstra 算法也一定能求出源点到所有点的最短路。

D. 当图中不存在负权边时,调用一次 Dijkstra 算法不能用于每对顶点间最短路计算。

答案:ABD

解析:dijstra算法不适用于负权图, 没选A, 应该是肯定。

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

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

解析:

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

#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

解析:看代码的意思是求有多少对子串是相同的,但是我看漏掉了一个(l1 != l2 || r1 != r2)的条件,结果算出来48.寄

#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 分)

解析:第一个可以手算出来,第二个样例太大了,看大佬说可以用康拓展开,蒟蒻表示先放着…

五、完善程序(共 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;

}

解析:

其他空都比较好填 ,只有那个

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

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

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

}

比较费脑子。题目里说是双向链表

可以看看这道

49. 二叉搜索树与双向链表 - AcWing题库

\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 <cstdio>

#include <algorithm>

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;

}

解析:最后一道的话,能蒙多少算多少,放个大佬的解析

NOIP 2018 提高组初赛试题 题目+答案+简要解析 - lzylzy/kk - 博客园 (cnblogs.com)

orz

代码分为两个部分。

第一部分是一个贪心,我们假设满足了优惠条件,按照折后价格进行贪心,如果结果满足了优惠条件就直接输出,此时如果放弃某些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商店买