浙江大学计算机与软件学院2019年保研上机模拟练习

7-1 Happy Numbers (20 分)

时间限制:400 ms 内存限制:64 MB

A happy number is defined by the following process: Starting with any positive integer, replace the number by the sum of the squares of its digits in base-ten, and repeat the process until the number either equals 1 (where it will stay), or it loops endlessly in a cycle that does not include 1. Those numbers for which this process ends in 1 are happy numbers and the number of iterations is called the degree of happiness, while those that do not end in 1 are unhappy numbers (or sad numbers). (Quoted from Wikipedia)

For example, 19 is happy since we obtain 82 after the first iteration, 68 after the second iteration, 100 after the third iteration, and finally 1. Hence the degree of happiness of 19 is 4.

On the other hand, 29 is sad since we obtain 85, 89, 145, 42, 20, 4, 16, 37, 58, and back to 89, then fall into an endless loop. In this case, 89 is the first loop number for 29.

Now your job is to tell if any given number is happy or not.

Input Specification:

Each input file contains one test case. For each case, the first line gives a positive integer N (≤100). Then N lines follow, each contains a positive integer (no more than 104) to be tested.

Output Specification:

For each given number, output in a line its degree of happiness if it is happy, or the first loop number if it is sad.

Sample Input:

3
19
29
1

Sample Output:

4
89
0

解析:

1、按照题目的要求一步步来即可。

2、判重用unordered_set,取各位的值既可以用10除取余,也可以转成字符串。

#include <unordered_set>
#include <cmath>
#include <iostream>
using namespace std;

void test() {
    int n, number, degree, i, sum = 0;  // degree计快乐度
    string s;
    scanf("%d", &n);
    for (i = 0; i < n; i++) {
        scanf("%d", &number);
        degree = 0;
        unordered_set<int> set;
        while (number != 1) {
            set.insert(number);
            sum = 0;
            for (auto item : to_string(number)) {
                sum += pow((item - 48), 2);
            }
            degree++;
            if (set.count(sum)) break;
            number = sum;
        }
        if (number == 1) printf("%d\n", degree);
        else printf("%d\n", sum);
    }
}

int main() {
    test();
    return 0;
}

7-2 Zigzag Sequence (25 分)

时间限制:200 ms 内存限制:64 MB

This time your job is to output a sequence of N positive integers in a zigzag format with width M in non-decreasing order. A zigzag format is to fill in the first row with M numbers from left to right, then the second row from right to left, and so on and so forth. For example, a zigzag format with width 5 for numbers 1 to 13 is the following:

1 2 3 4 5
10 9 8 7 6
11 12 13

Input Specification:

Each input file contains one test case. For each case, the first line gives 2 positive integers N and M. Then the next line contains N positive integers as the original sequence. All the numbers are no more than 104. The numbers in a line are separated by spaces.

Output Specification:

For each test case, output the sequence in the zigzag format with width M in non-decreasing order. There must be exactly 1 space between two adjacent numbers, and no extra space at the beginning or the end of each line.

Sample Input 1:

14 5
37 76 98 20 98 76 42 53 99 95 60 81 58 93

Sample Output 1:

20 37 42 53 58
93 81 76 76 60
95 98 98 99

Sample Input 2:

15 4
96 37 76 98 20 98 76 42 53 99 95 60 81 58 93

Sample Output 2:

20 37 42 53
76 76 60 58
81 93 95 96
99 98 98

解析:

1、先求出一共要打印多少行。

2、根据当前行是奇数还是偶数,判断要不要反过来打印,还要考虑当前行是否不足M个。

假设行i从0开始,则第i行的正向下标范围是[i * M, i * M + M),因此反向打印的下标为[i * M + M, i * M]。再考虑到数组有上界,故正向时的范围上界(开区间)应该在i * M + M与N中取较小值,反向时范围下界(闭区间)应该在i * M + M与N - 1中取较小值。

3、打印每行时还要注意最后一个数字后不能带空格。

4、也可以用reverse数组进行反转,同样要考虑数组上界。

#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;

void test() {
    int N, M, i, j, line;
    vector<int> input;
    vector<vector<int>> ans;
    scanf("%d %d", &N, &M);
    input.resize(N);
    line = N % M ? N / M + 1 : N / M;
    for (i = 0; i < N; i++) scanf("%d", &input[i]);
    sort(input.begin(), input.end());

    int start, end;
    for (i = 0; i < line; i++) {
        if (i % 2 == 0) {
            end = min(M + i * M, N);
            for (j = i * M; j < end; j++) {
                printf("%d", input[j]);
                if (j < end - 1) printf(" ");
            }
        } else {
            start = min(M - 1 + i * M, N - 1);
            for (j = start; j >= i * M; j--) {
                printf("%d", input[j]);
                if (j > i * M) printf(" ");
            }
        }
        printf("\n");
    }
}

int main() {
    test();
    return 0;
}

7-3 Is It An AVL Tree (25 分)

时间限制:400 ms 内存限制:64 MB

In computer science, an AVL tree (Georgy Adelson-Velsky and Evgenii Landis' tree, named after the inventors) is a self-balancing binary search tree. It was the first such data structure to be invented. In an AVL tree, the heights of the two child subtrees of any node differ by at most one. (Quoted from wikipedia)

For each given binary search tree, you are supposed to tell if it is an AVL tree.

Input Specification:

Each input file contains several test cases. The first line gives a positive integer K (≤10) which is the total number of cases. For each case, the first line gives a positive integer N (≤30), the total number of nodes in the binary search tree. The second line gives the preorder traversal sequence of the tree with all the keys being distinct. All the numbers in a line are separated by a space.

Output Specification:

For each test case, print in a line "Yes" if the given tree is an AVL tree, or "No" if not.

Sample Input:

3
7
50 40 36 48 46 62 77
8
50 40 36 48 46 62 77 88
6
50 40 36 48 46 62

Sample Output:

Yes
No
No

解析:

1、先用前序+中序建树。

2、递归求出每个结点的左子树和右子树的高度。如果发现当前结点不平衡,就退出递归,不用判断其它结点了;如果平衡,则当前结点的高度为左右子树中较高的+1。

#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;

typedef struct Node {
    int data, height = -1;
    struct Node *l = NULL, *r = NULL;
} Node;

Node *root = NULL;
int N;
vector<int> pre, in;

Node *create (int pre1, int pre2, int in1, int in2) {
    if (pre1 > pre2) return NULL;
    int i = in1;
    while (in[i] != pre[pre1]) i++;
    int len = i - in1;
    Node *node = new Node;
    node->data = in[i];
    node->l = create(pre1 + 1, pre1 + len, in1, i - 1);
    node->r = create(pre1 + len + 1, pre2, i + 1, in2);
    return node;
}

int flag = 0;  // 当前树是否AVL

int getHeight (Node *node) {
    // 当flag == 1时表示已经发现一个结点不平衡,没有必要再判断其它结点了
    if (flag == 1 || node == NULL) return 0;
    if (node->height == -1) {
        int left = getHeight(node->l);
        int right = getHeight(node->r);
        if (abs(left - right) > 1) {
            flag = 1;
            return 0;
        }
        node->height = max(left, right) + 1;
    }
    return node->height;
}

void test() {
    int K, i, j;
    scanf("%d", &K);
    for (i = 0; i < K; i++) {
        scanf("%d", &N);
        pre.clear(); pre.resize(N);
        for (j = 0; j < N; j++) scanf("%d", &pre[j]);
        in = pre;
        sort(in.begin(), in.end());
        root = create(0, N- 1, 0, N- 1);
        flag = 0;  // 假设是AVL树,一旦发现有一个节点不是平衡的,就不是AVL树
        getHeight(root);
        printf("%s\n", flag ? "No" : "Yes");
    }
}

int main() {
    test();
    return 0;
}

7-4 Index of Popularity (30 分)

时间限制:1000 ms 内存限制:64 MB

The index of popularity (IP) of someone in his/her circle of friends is defined to be the number of friends he/she has in that circle. Now you are supposed to list the members in any given friend circle with top 3 IP's.

Input Specification:

Each input file contains one test case. Each case starts with a line containing 2 positive integers N and M (both no more than 105), which are the total number of people and the number of friend relations, respectively. Hence the people here are numbered from 1 to N.

Then M lines follow, each contains the indices of a pair of friends, separated by a space. It is assumed that if A is a friend of B, then B is a friend of A.

Then several queries follow, each occupies a line. For each line of query, K (3≤KN), the total number of members in this friend circle is given first, with K indices of members follow. It is guaranteed that all the indices in a circle are distinct.

The input ends when K is zero, and this case must NOT be processed.

Output Specification:

For each query, print in a line the members with top 3 indices of popularity in descending order of their IP's. If there is a tie, output the one with the smaller number. The numbers must be separated by exactly 1 space, and there must be no extra space at the beginning or the end of the line.

Sample Input:

8 10
2 1
1 3
1 4
1 5
5 8
3 5
2 3
6 3
4 6
3 4
7 8 1 2 3 4 6 5
4 1 3 5 2
4 8 7 4 2
0

Sample Output:

3 1 4
1 3 2
2 4 7

解析:

麻烦的一题,容易超时,下一段是碎碎念,可以跳过不看:

105*105的邻接矩阵太大了,只能用邻接表。一开始邻接表用unordered_set数组,对每个查询集合Q,把Q里每个成员的联系人从邻接表里取出来形成一个S,再看看集合Q的其他人是否在S里,做法极其暴力,两重循环+搜索,果然超时了,而且超时了2个6分的测试点。接着改进做法,既然要求两个集合之交,先排序,两个序列一起扫一遍就好了,线性复杂度。没想到还是有一个超时。后来才观察出来更简单的做法。上网一搜题解,有人一下子就想到了这个方法,觉得这个题很简单,我的内心是崩溃的……

方法一:对每个查询集合Q,统计每个成员所有相连的另一个成员出现次数,然后只对Q包含的成员进行排序。

#include <vector>
#include <unordered_map>
#include <algorithm>
#include <iostream>
using namespace std;

const int maxn = 100002;

typedef struct Node {
    int index, count;
} Node;

int N, M;
vector<int> G[maxn];

bool cmp (Node a, Node b) {
    if (a.count != b.count) return a.count > b.count;
    return a.index < b.index;
}

void test() {
    int i, t1, t2, K;
    scanf("%d %d", &N, &M);
    for (i = 0; i < M; i++) {
        scanf("%d %d", &t1, &t2);
        G[t1].push_back(t2);
        G[t2].push_back(t1);
    }

    scanf("%d", &K);
    while (K) {
        vector<Node> degree(K);
        for (i = 0; i < K; i++) {
            scanf("%d", &degree[i].index);
        }
        unordered_map<int, int> m;
        for (i = 0; i < K; i++) {
            vector<int> &temp = G[degree[i].index];
            for (auto item : temp) {
                m[item]++;
            }
        }
        for (auto &item : degree) {
            item.count = m[item.index];
        }
        sort(degree.begin(), degree.end(), cmp);
        for (i = 0; i < 2; i++) printf("%d ", degree[i].index);
        printf("%d\n", degree[i].index);
        scanf("%d", &K);
    }
}

int main() {
    test();
    return 0;
}

方法二:存储所有边然后遍历,若查询集合Q中存在该边,则两端的结点的度都加1。

//
// Created by niko on 2020/9/19.
//
//剩余时间:19:44

#include <bits/stdc++.h>

using namespace std;
/**
 * 题意:给出n个点,m条边的图,接下来进行几次查询,每次查询问由k个结点组成的子图中
 * 前三个度数最高的结点编号(度数降序排序),若度数相同,则按照从小到大的顺序输出
 * @return
 */
vector<pair<int,int>> edge;//存储每条边
unordered_map<int,int> degree;//子图中每个结点的度数
//排序规则
bool cmp(int a,int b){
    if(degree[a]!=degree[b])return degree[a]>degree[b];
    return a<b;
}
int main(){
    //加速cin
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n,m,k,a,b;
    cin>>n>>m;
    while (m--){
        cin>>a>>b;
        edge.push_back({a,b});
    }
    while (cin>>k&&k!=0){
        degree.clear();
        vector<int> v(k);//存储子图中每个结点
        unordered_map<int,bool> exist;//是否存在
        for(int i=0;i<k;i++){
            cin>>v[i];
            exist[v[i]]= true;
        }
        //核心,遍历每条边,若子图中存在该边,则两端的结点的度都加1
        for(auto it:edge){
            if(exist[it.first]&&exist[it.second]){
                degree[it.first]++;
                degree[it.second]++;
            }
        }
        sort(v.begin(),v.end(),cmp);
        printf("%d %d %d\n",v[0],v[1],v[2]);
    }

    return 0;
}

还是太菜了我,且学且进步吧。

参考资料

浙江大学计算机与软件学院2019年保研上机模拟练习7-4 Index of Popularity (30分)

上一篇:2021-05-15


下一篇:文本与图