NOIP2016提高组初赛难点整理

选择题

  1. 如果开始时计算机处于小写输入状态,现在有一只小老鼠反复按照 CapsLock、字母键 A A A、字母键 S S S 和字母键 D D D 的顺序来回按键,即 CapsLock、A、S、D、S、A、CapsLock、A、S、D、S、A、CapsLock、A、S、D、S、A…,屏幕上输出的第 81 81 81 个字符是字母(A)。

【解析】 5 5 5个字母一组,单数组为大写,双数组为小写。第 81 81 81个字母是第 17 17 17组第 1 1 1个,所以是A

  1. 与二进制小数 0.1 0.1 0.1 相等的八进制数是( 0.4)。

【解析】 3 3 3位二进制数可以转换为 1 1 1位八进制数,所以 ( 0.100 ) 2 = ( 0.4 ) 8 (0.100)_2=(0.4)_8 (0.100)2=(0.4)8

  1. 某计算机的 CPU 和内存之间的地址总线宽度是 32 位 (bit) ,这台计算机最多可以使用(4GB)的内存。

【解析】 32 32 32位操作系统可以寻址 2 32 2^{32} 232个字节的内存范围, 64 64 64位操作系统则可寻址 2 64 2^{64} 264个字节的内存范围。换句话说,32位的操作系统理论上只能使用约4GB的内存,要是程序想使用超过4GB的内存,那么就要选择64位的操作系统。

  1. 假设某算法的计算时间表示为递推关系式: T ( n ) = 2 T ( n 4 ) + n \begin{aligned}&T(n)=2T(\frac{n}{4}) + \sqrt{n}\end{aligned} T(n)=2T(4n)+n T ( 1 ) = 1 T(1) = 1 T(1)=1,则算法的时间复杂度为( n l o g n \sqrt{n}log^n n logn )。

【解析】代入法求时间复杂度:
T ( n ) = 2 T ( n 4 ) + n = 2 ( 2 T ( n 16 ) + n 2 ) + n = 4 T ( n 16 ) + 2 n = . . . T(n)=2T(\frac{n}{4}) + \sqrt{n} = 2(2T(\frac{n}{16})+\frac{\sqrt{n}}{2}) + \sqrt{n} =4T(\frac{n}{16}) + 2\sqrt{n}=... T(n)=2T(4n)+n =2(2T(16n)+2n )+n =4T(16n)+2n =...
一共有 l o g n log^n logn n \sqrt{n} n ,所以时间复杂度为 n l o g n \sqrt{n}log^n n logn

问题求解

  1. 一个 1 × 8 1 \times 8 1×8 的方格图形(不可旋转)用黑、白两种颜色填涂每个方格。如果每个方格只能填涂一种颜色,且不允许两个黑格相邻,共有(55)种填涂方案。

【解析】分情况讨论,使用插空法计算:
0 0 0个黑色方格:1
1 1 1个黑色方格:8
2 2 2个黑色方格, 6 6 6个白色方格,有 7 7 7个空可以插入: C 7 2 = C_7^2= C72=21
3 3 3个黑色方格, 5 5 5个白色方格,有 6 6 6个空可以插入: C 6 3 = C_6^3= C63=20
4 4 4个黑色方格, 4 4 4个白色方格,有 5 5 5个空可以插入: C 5 4 = C_5^4= C54=5

总方案数 = 1 + 8 + 21 + 20 + 5 = 1+8+21+20+5= 1+8+21+20+5=55

阅读程序

#include<iostream>
using namespace std;

int lps(string seq, int i, int j) {
    int len1, len2;
    if (i == j)
        return 1;
    if (i > j)
        return 0;
    if (seq[i] == seq[j])
        return lps(seq, i + 1, j - 1) + 2;
    len1 = lps(seq, i, j - 1);
    len2 = lps(seq, i + 1, j);
    if (len1 > len2)
        return len1;
    return len2;
}

int main() {
    string seq = "acmerandacm";
    int n = seq.size();
    cout << lps(seq, 0, n - 1) << endl;
    return 0;
}

【解析】本题递归求解前缀序列(从0开始、自左向右)和后缀,序列(从n-1开始、自右向左)的相同字母的个数。 acmerandacm,此外还要加上一个公共字符。最后答案为5

#include <iostream>
#include <cstring>
using namespace std;

int map[100][100];
int sum[100], weight[100];
int visit[100];

int n;

void dfs(int node) {
    visit[node] = 1;
    sum[node] = 1;
    int v, maxw = 0;
    for (v = 1; v <= n; v++) {
        if (!map[node][v] || visit[v])
            continue;
        dfs(v);
        sum[node] += sum[v];
        if (sum[v] > maxw)
            maxw = sum[v];
    }
    if (n - sum[node] > maxw)
        maxw = n - sum[node];
    weight[node] = maxw;
}

int main() {
    memset(map, 0, sizeof(map));
    memset(sum, 0, sizeof(sum));
    memset(weight, 0, sizeof(weight));
    memset(visit, 0, sizeof(visit));
    cin >> n;
    int i, x, y;
    for (i = 1; i < n; i++) {
        cin >> x >> y;
        map[x][y] = 1;
        map[y][x] = 1;
    }
    dfs(1);
    int ans = n, ansN = 0;
    for (i = 1; i <= n; i++)
        if (weight[i] < ans) {
            ans = weight[i];
            ansN = i;
        }
    cout << ansN << " " << ans << endl;
    return 0;
}

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

【解析】树的深度优先遍历求重心。
树的重心定义:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中结点个数的最大值最小,那么这个节点被称为树的重心。
本题输入数据所表示的树如下图所示:
在这里插入图片描述
该树的重心为 2 2 2号结点,删除2号结点后最大的连通块为绿色部分,共 5 5 5个结点。
答案:2 5

完善程序

(交朋友)根据社会学研究表明,人们都喜欢找和自己身高相近的人做朋友。 现在有 n n n 名身高两两不相同的同学依次走入教室,调查人员想预测每个人在走入教室的瞬间最想和已经进入教室的哪个人做朋友。当有两名同学和这名同学的身高差一样时,这名同学会更想和高的那个人做朋友。比如一名身高为 1.80 1.80 1.80 米的同学进入教室时,有一名身高为 1.79 1.79 1.79 米的同学和一名身高为 1.81 1.81 1.81 米的同学在教室里,那么这名身高为 1.80 1.80 1.80 米的同学会更想和身高为 1.81 1.81 1.81 米的同学做朋友。对于第一个走入教室的同学我们不做预测。

由于我们知道所有人的身高和走进教室的次序,所以我们可以采用离线的做法来解决这样的问题,我们用排序加链表的方式帮助每一个人找到在他之前进入教室的并且和他身高最相近的人。

#include <iostream>
using namespace std;
#define MAXN 200000
#define infinity 2147483647

int answer[MAXN], height[MAXN], previous[MAXN], next[MAXN]; 
int rank[MAXN];
int n;

void sort(int l, int r) {
    int x = height[rank[(l + r) / 2]], i = l, j = r, temp;
    while (i <= j)
    {
        while (height[rank[i]] < x) i++;
        while (height[rank[j]] > x) j--;
        if ( ___(1)___ ) {
            temp = rank[i]; rank[i] = rank[j]; rank[j] = temp;
             i++; j--;
        }
    }
    if (i < r) sort(i, r);
    if (l < j) sort(l, j);
}

int main()
{
    cin >> n;
    int i, higher, shorter;
    for (i = 1; i <= n; i++) {
        cin >> height[i];
        rank[i] = i;
    }
    sort(1, n);
    for (i = 1; i <= n; i++) {
        previous[rank[i]] = rank[i - 1];
           ___(2)___ ;
    }
    for (i = n; i >= 2; i--){
        higher = shorter = infinity;
        if (previous[i] !=0)
            shorter = height[i] - height[previous[i]];
        if (next[i] != 0)
            ___(3)___ ;
        if ( ___(4)___ )
            answer[i] = previous[i];
        else
            answer[i] = next[i];
        next[previous[i]] = next[i];
        ___(5)___ ;
    }
    for (i = 2; i <= n; i++)
        cout << i << ":" << answer[i];
    return 0;
}

【解析】题目中已经提示通过排序加链表的方式进行处理,所以需要搞清楚rank[]previous[]next[]的记录的信息。
rank[i] = j表示身高排名i的同学在height[]的位置。
previous[i]表示身高仅次于height[i]的同学在height[]的位置。
next[i]表示身高仅高于height[i]的同学在height[]的位置。

  • 空①,快速排序,将排名的位置信息保存到数组rank中。ij是快速排序的两个哨兵指针,此空应填:i <= j
  • 空②,排序之后,建立身高顺序的链表,此空应填: next[rank[i]] = rank[i + 1]
  • 空③,从后向前,查找每个同学的朋友。如果存在比第i个身高还要高的人,计算身高差值: heigher = height[next[i]] - heigh[i]
  • 空④,选择身高差值较小的人做朋友,shorter < heigher
  • 空⑤,因为是从后向前处理,所以第i个同学处理完毕后,将他从链表中删除,相当于他还没有出现。previous[next[i]] = previous[i]
  1. (交通中断)有一个小国家,国家内有 n n n 座城市和 m m m 条双向的道路,每条道路连接着两座不同的城市。其中 1 1 1 号城市为国家的首都。由于地震频繁可能导致某一个城市与外界交通全部中断。这个国家的首脑想知道,如果只有第 i ( i > 1 ) i(i>1) i(i>1) 个城市因地震而导致交通中断时,首都到多少个城市的最短路径长度会发生改变。如果因为无法通过第 i i i 个城市而导致从首都出发无法到达某个城市,也认为到达该城市的最短路径长度改变。
    对于每一个城市 i i i,假定只有第 i i i 个城市与外界交通中断,输出有多少个城市会因此导致到首都的最短路径长度改变。
    我们采用邻接表的方式存储图的信息,其中 head[x] 表示顶点 x 的第一条 边的编号,next[i] 表示第 i 条边的下一条边的编号, point[i] 表示第 i 条边的终点,weight[i] 表示第 i条边的长度。
#include <iostream>
#include <cstring>
using namespace std;
#define MAXN 6000
#define MAXM 100000
#define infinity 2147483647

int head[MAXN], next[MAXM], point[MAXM], weight[MAXM];
int queue[MAXN], dist[MAXN], visit[MAXN];

int n, m, x, y, z, total = 0, answer;

void link(int x,int y,int z) {
    total++;
    next[total] = head[x]; head[x] = total; point[total] = y; weight[total] = z; total++;
    next[total] = head[y]; head[y] = total; point[total] = x; weight[total] = z;
}

int main() {
    int i, j, s, t; cin >> n >> m;
    for (i = 1; i <= m; i++) {
        cin >> x >> y >> z;
        link(x, y, z);
    }
    for (i = 1; i <= n; i++) dist[i] = infinity;
    ___(1)___ ;
    queue[1] = 1;
    visit[1] = 1;
    s = 1;
    t = 1;
    // 使用 SPFA 求出第一个点到其余各点的最短路长度
    while (s <= t) {
        x = queue[s % MAXN];
        j = head[x];
        while (j != 0) {
            if ( ___(2)___ ) {
                dist[point[j]] = dist[x] + weight[j];
                if (visit[point[j]] == 0) {
                    t++;
                    queue[t % MAXN] = point[j];
                    visit[point[j]] = 1;
                }
            }
            j = next[j];
        }
        ___(3)___ ;
        s++;
    }
    for (i = 2; i <= n; i++) {
        queue[1] = 1;
        memset(visit, 0, sizeof(visit));
        visit[1] = 1;
        s = 1;
        t = 1;
        while (s <= t) { // 判断最短路长度是否不变
            x = queue[s];
            j = head[x];
            while (j != 0) {
                if (point[j] != i && ___(4)___ &&visit[point[j]] == 0) {
                    ___(5)___ ;
                    t++;
                    queue[t] = point[j];
                }
                j = next[j];
            }
            s++;
        }
        answer = 0;
        for (j = 1; j <= n; j++)
            answer += 1 - visit[j];
        cout << i << ":" << answer - 1 << endl;
    }
    return 0;
}

【解析】根据题目提示,使用 SPFA 求出第1个点到其余各点的最短路长度。

  • 空①,初始化1点到其自己的距离,dist[1] = 0
  • 空②,判断是否能借助x点,缩短源点到point[j]的最短距离,dist[point[j]] > dist[x] + weight[j]
  • 空③,x点出队,相应的将visit[x] = 0
  • 空④,判断到点point[j]的最短路长度是否不变,dist[point[j]] == dist[x] + weight[j]
  • 空⑤,如果到点point[j]的最短路长度没有变化,那么visit[point[j]] = 1,用于计数统计。

猜你喜欢

转载自blog.csdn.net/qiaoxinwei/article/details/108117918