基础算法整理

基础算法整理

课程:数据结构与算法

Author:RedamancyXun



1. 两个整数a,b之和

  • 描述

    输入两个整数 a,b,输出两个整数的和。

  • 输入描述

    第一行输入一个整数 T,表示需要计算的次数。接下来 T 行,每行输入两个用空格分隔的整数 a,b。

  • 输出描述

    对于每次输入的 a, b,输出 a + b 的值。结果保证在 32 位整型(int)范围内。

  • 参考代码

    #include <iostream>
    #include <stdio.h>
    using namespace std;
    
    int main() {
        int T;
        int a, b;
        cin >> T;
        
        while(scanf("%d %d", &a, &b) != EOF) {
            cout << a + b << endl;
        }
        
        return 0;
    }
    

2. 斐波那契数列

  • 描述

    输入一个 n,求出斐波那契数列f(n)对 1000000007(109+7) 取模结果

  • 输入描述

    输入一个整数 n(1≤n≤100000)

  • 输出描述

    f(n) mod 1000000007 的值

  • 参考代码

    #include <stdio.h>
    
    int main() {
        int a = 1;
        int b = 1;
        int n;
        scanf("%d", &n);
        
        if (n == 1 || n == 2) {
            printf("1");
        }
        
        for (int i = 0; i < n - 2; i++) {
            int temp = b;
            b = (a + b) % 1000000007;
            a = (temp) % 1000000007;
        }
        
        printf("%d", b);
        
        return 0;
    }
    

3. 矩阵旋转

  • 描述

    给出一个n×m 的整数矩阵,请你把这个矩阵顺时针旋转 90度以后输出。

  • 输入描述

    第一行输入两个整数 n,m(1≤n,m≤200),用空格隔开。接下来 n 行,每行输入 m个整数,表示输入的矩阵。矩阵中元素都是 int 范围内的整数。

  • 输出描述

    输出 m 行,每行 n个空格隔开的整数,表示旋转以后的矩阵。
    注意:每行末尾不能输出多余的空格。

  • 参考代码

    #include <stdio.h>
    
    int main(){
        int n, m;
        scanf("%d %d", &n, &m);
        int a[200][200];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                scanf("%d", &a[j][n - i - 1]);
            }
        }
        for (int i = 0; i < m; i++) {
            printf("%d", a[i][0]);
            for (int j = 1; j < n; j++) {
                printf(" %d", a[i][j]);
            }
            printf("\n");
        }
        return 0;
    }
    

4. 最大子阵

  • 描述

    给定一个 n \times mn×m 的矩阵 AA,求 AA 中的一个非空子矩阵,使这个子矩阵中的元素和最大。其中,AA 的子矩阵指在 AA 中行和列均连续的一部分。

  • 输入描述

    输入的第一行包含两个整数n,m(1≤n,m≤50),分别表示矩阵 A 的行数和列数。
    接下来 n 行,每行 m 个整数,表示矩阵Ai,j(−1000≤A i,j≤1000)。

  • 输出描述

    输出一行,包含一个整数,表示 A 中最大子矩阵的元素和。

  • 参考代码

    #include<iostream>
    using namespace std;
    
    int maxMatrix(int (&maxtrix)[50][50], int n, int m) {
        int dp[50][50];
        int maxSum = maxtrix[0][0];
    
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                dp[i][j] = maxtrix[i][j];
    
                if (i == 0 && j > 0) {
                    dp[i][j] += dp[i][j - 1];
                }
                if (j == 0 && i > 0) {
                    dp[i][j] += dp[i - 1][j];
                }
                if (j > 0 && i > 0) {
                    dp[i][j] += dp[i][j - 1] + dp[i - 1][j] - dp[i - 1][j - 1];
                }
    
                if (dp[i][j] > maxSum) {
                    maxSum = dp[i][j];
                }
    
                for (int x = 0; x < i; x++) {
                    if (dp[i][j] - dp[x][j] > maxSum) {
                        maxSum = dp[i][j] - dp[x][j];
                    }
                }
    
                for (int y = 0; y < j; y++) {
                    if (dp[i][j] - dp[i][y] > maxSum) {
                        maxSum = dp[i][j] - dp[i][y];
                    }
                }
    
                for (int x = 0; x < i; x++) {
                    for (int y = 0; y < j; y++) {
                        if (dp[i][j] - dp[x][j] - dp[i][y] + dp[x][y] > maxSum) {
                            maxSum = dp[i][j] - dp[x][j] - dp[i][y] + dp[x][y];
                        }
                    }
                }
            }
        }
    
        return maxSum;
    }
    
    int main() {
        int n, m;
        int matrix[50][50];
        cin >> n >> m;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                cin >> matrix[i][j];
            }
        }
        cout << maxMatrix(matrix, n, m);
    }
    

5. 四平方和

  • 描述

    四平方和定理,又称为拉格朗日定理:每个正整数都可以表示为至多四个正整数的平方和。如果把 0 包括进去,就正好可以表示为四个数的平方和。即对于一个给定的正整数 n,可以表示为:n = a2 + b2 + c2 + d2。
    你需要求出 字典序 最小的一组解 a,b,c,d。字典序大小:从左到右依次比较,如果相同则比较下一项,直到有一项不同,较小的一方字典序更小,反之字典序更大,所有项均相同则二者字典序相同。

  • 输入描述

    程序输入为一个正整数 N(1≤N≤5000000)

  • 输出描述

    输出四个非负整数 a,b,c,d,中间用空格分开

  • 参考代码

    #include<stdio.h>
    #include<math.h>
    
    int main(){
        int n;
        int a = 0;
        int b = 0;
        int c = 0;
        int d = 0;
        scanf("%d", &n);
        
        for (int i = 0; i <= (int)sqrt(n); i++) {
            a = i;
            for (int j = 0; j <= (int)sqrt(n - a*a); j++) {
                b = j;
                for (int x = 0; x <= (int)sqrt(n - a*a - b*b); x++) {
                	c = x;
                	d = (int)sqrt(n - a*a - b*b - c*c);
                	if (n - a*a - b*b - c*c - d*d == 0) {
    	       			printf("%d %d %d %d", a, b, c, d);
     	       			return 0;
               		}
            	}
            }  
        }
        return 0;
    }
    

6. A*B问题

  • 描述

    计算两个非负整数A,B 的乘积,A,B 可能会很大。

  • 输入描述

    第一行输入一个非负整数 A。
    第二行输入一个非负整数 B。
    A,B 的长度不大于 500。

  • 输出描述

    输出 A×B 的值。

  • 参考代码

    #include<iostream>
    #include<cstring>
    using namespace std;
    
    int main() {
    	string a, b;
    	int result[1005] = { 0 };
    	int start = 0;
    	cin >> a >> b;
    
    	for (int i = a.size() - 1; i >= 0; i--) {
    		for (int j = b.size() - 1; j >= 0; j--) {
    			int num1 = (int)(a[i] - '0');
    			int num2 = (int)(b[j] - '0');
    
    			result[i + j + 1] += (num1 * num2) % 10;
    			result[i + j] += (num1 * num2) / 10;
    
    			result[i + j] += result[i + j + 1] / 10;
    			result[i + j + 1] %= 10;
    		}
    	}
    
        while (start < a.size() + b.size() && result[start] == 0) {
            start++;
        }
    
        if (start == a.size() + b.size()) {
            cout << 0;
            return 0;
        }
    
    	for (int i = start; i < a.size() + b.size(); i++) {
    		cout << result[i];
    	}
    
    	return 0;
    }
    

7. 得到整数 X

  • 描述

    小明有 n 个互不相同的正整数,现在他要从这 n个正整数之中无重复地选取任意个数,并仅通过加法凑出整数 X。求小明有多少种不同的方案来凑出整数 X。

  • 输入描述

    第一行,输入两个整数 n,X(1≤n≤20,1≤X≤2000)。
    接下来输入 n个整数,每个整数不超过 100。

  • 输出描述

    输出一个整数,表示能凑出 X 的方案数。

  • 参考代码

    #include<iostream>
    using namespace std;
    
    int countWays(int(&number)[20], int n, int X) {
    	int bp[2001] = { 0 };
    	bp[0] = 1;
    	for (int i = 0; i < n; i++) {
    		for (int j = X; j >= number[i]; j--) {
    			bp[j] += bp[j - number[i]];
    		}
    	}
    	return bp[X];
    }
    
    int main() {
    	int n, X;
    	int number[20];
    	cin >> n >> X;
    	for (int i = 0; i < n; i++) {
    		cin >> number[i];
    	}
    	cout << countWays(number, n, X);
    	return 0;
    }
    

8. 打印锯齿矩阵(STL)

  • 描述

    锯齿矩阵是指每一行包含的元素个数不相同的矩阵,比如:

    3 5 2 6 1
    2 3 4
    1 6 2 7

    读入若干对整数 (x,y)(x,y),表示在第 x 行的末尾加上一个元素 y。输出最终的锯齿数组。初始时矩阵为空。

  • 输入描述

    第第一行输入两个整数 n,m(1≤n,m≤10000),其中 n 表示锯齿数组的行数,m 表示插入的元素总数。

    接下来一共 m 行,每行两个整数x,y(1≤x≤n,0≤y≤10000),表示在第 x 行的末尾插入一个元素 y。

  • 输出描述

    一共输出 n 行,每行若干个用空格分隔的整数。如果某行没有任何元素,则输出一个空行。

  • 参考代码

    #include <iostream>
    #include <vector>
    using namespace std;
    
    int main() {
        int n, m;
        cin >> n >> m;
    
        vector<vector<int>> matrix(n);
    
        for (int i = 0; i < m; ++i) {
            int x, y;
            cin >> x >> y;
            matrix[x - 1].push_back(y);
        }
    
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < matrix[i].size(); ++j) {
                cout << matrix[i][j];
                if (j < matrix[i].size() - 1) {
                    cout << " ";
                }
            }
            cout << endl;
        }
    
        return 0;
    }
    

9. 堆积木(STL)

  • 描述

    蒜头有 n 块积木,编号分别为 1 到 n。一开始,蒜头把第 i 块积木放在位置 i。蒜头君进行 m 次操作,每次操作,蒜头把位置 b 上的积木整体移动到位置 a 上面。比如 1 位置的积木是 1,2 位置的积木是 2,那么把位置 2 的积木移动到位置 1 后,位置 1 上的积木从下到上依次为1,2。

  • 输入描述

    第一行输入 2 个整数n,m(1≤n≤10000,0≤m≤10000)。

    接下来 m 行,每行输入 2 个整数 a,b(1≤a,b≤n),如果a,b 相等则本次不需要移动。

  • 输出描述

    输出 n 行,第 i 行输出位置 i 从下到上的积木编号,如果该行没有积木输出一行空行。(&#10;表示的是空行,实际程序代码用\n)

  • 参考代码

    #include <iostream>
    #include <vector>
    using namespace std;
    
    int main() {
        int n, m;
        cin >> n >> m;
        
        vector<vector<int>> nums(n + 1);
        for (int i = 1; i <= n; i++) {
            nums[i].push_back(i);
        }
        for (int i = 0; i < m; i++) {
            int a, b;
            cin >> a >> b;
            if (a == b)
                continue;
            move(nums[b].begin(), nums[b].end(), back_inserter(nums[a]));
            nums[b].clear();
        }
        for (int i = 1; i <= n; i++) {
            if (nums[i].empty())
                cout << endl;
            else {
                for (int j = 0; j < nums[i].size(); j++) {
                    cout << nums[i][j] << " ";
                }
                cout << endl;
            }
        }
        return 0;
    }
    

10. 计算集合的并集(STL)

  • 描述

    给你两个集合,计算其并集,即 {A}+{B}。

    注:{A}+{B} 中不允许出现重复元素,但是 {A} 与 {B} 之间可能存在相同元素。

  • 输入描述

    输入数据分为三行,第一行有两个数字 n,m(0<n,m≤10000),分别表示集合 A 和集合 B 的元素个数。后两行分别表示集合 A 和集合 B。每个元素为不超出 int 范围的整数,每个元素之间用一个空格隔开。

  • 输出描述

    输出一行数据,表示合并后的集合,要求从小到大输出,每个元素之间用一个空格隔开。

  • 参考代码

    #include <iostream>
    #include <set>
    using namespace std;
    
    int main() {
        int n, m;
        set<int> result;
        cin >> n >> m;
        
        for (int i = 0; i < n + m; i++) {
            int item;
            cin >> item;
            result.insert(item);
        }
        
        set<int> :: iterator it = result.begin();
        cout << *it;
        it++;
        for (;it != result.end(); it++) {
            cout << " " << *it;
        }
        
        return 0;
    }
    

11. 小明学英语(STL)

  • 描述

    小明快要考托福了,这几天,小明每天早上都起来记英语单词。花椰妹时不时地来考一考小 明:花椰妹会询问小明一个单词,如果小明背过这个单词,小明会告诉花椰妹这个单词的意 思,不然小明会跟花椰妹说还没有背过。单词是由连续的大写或者小写字母组成。注意单词 中字母大小写是等价的。比如You和you是一个单词。

  • 输入描述

    首先输入一个 n (1≤n≤100000) 表示事件数。接下来 n 行,每行表示一个事件。 每个事件输入为一个整数 d 和一个单词 word(单词长度不大于 20),用空格隔开。如果 d=0,表示小明记住了 word 这个单词,如果 d=1,表示这是一个 测试,测试小明是否认 识单词 word(花椰妹永远不会告诉小明这个单词的意思)。事件的输入是按照时间先后顺 序输入的。

  • 输出描述

    对于花椰妹的每次测试,如果小明认识这个单词,输出一行Yes, 否则输出一行No

  • 参考代码

    #include <iostream>
    #include <set>
    #include <string>
    #include <cctype>
    using namespace std;
    
    string lower(string str) {
        for (int i = 0; i < str.size(); i++)
            str[i] = tolower(str[i]);
        return str;
    }
    
    int main() {
        int n;
        set<string> memery;
        cin >> n;
        
        for (int i = 0; i < n; i++) {
            int a;
            string b;
            cin >> a >> b;
            if (a == 0)
                memery.insert(lower(b));
            if (a == 1 && memery.count(lower(b)) == 1)
                cout << "Yes" << endl;
            if (a == 1 && memery.count(lower(b)) == 0)
                cout << "No" << endl;
        }
        
        return 0;
    }
    

12. 小明面试(STL)

  • 描述

    小明去面试的时候,曾经遇到这样一个面试题:
    给定 n 个整数,求里面出现次数最多的数,如果有多个重复出现的数,求出值最大的一个。当时可算是给小明难住了。现在小明来考考你。

  • 输入描述

    第一行输入一个整数n(1≤n≤100000),接下来一行输入n个int 范围内的整数。

  • 输出描述

    输出出现次数最多的数和出现的次数,中间用一个空格隔开,如果有多个重复出现的数,输出值最大的那个。

  • 参考代码

    #include <map>
    #include <iostream>
    using namespace std;
    
    int main() {
        int n;
        int max = 0;
        int result;
        map<int, int> count;
        cin >> n;
        
        for (int i = 0; i < n; i++) {
            int num;
            cin >> num;
            if (count.count(num) == 0)
                count[num] = 1;
            else
                count[num]++;
            if (count[num] > max) {
                max = count[num];
                result = num;
            }
            else if (count[num] == max && num > result)
                result = num;
        }
        
        cout << result << " " << max;
        return 0;
    }
    

13. 水果店(STL)

  • 描述

    小明经营着一个不大的水果店。他认为生存之道就是经营最受顾客欢迎的水果。现在他想要 一份水果销售情况的明细表,这样就可以很容易掌握所有水果的销售情况了。小明告诉你每 一笔销售记录的水果名称,产地和销售的数量,请你帮他生成明细表。

  • 输入描述

    第一行是一个整数 N(0 < N≤1000),表示工有N 次成功的交易。其后有 N 行 数据,每行表示一次交易,由水果名称(小写字母组成,长度不超过 100),水果产地(小写字 母组成,长度不超过 100)和交易的水果数目(正整数,不超过 1000)组成.

  • 输出描述

    请你输出一份排版格式正确(请分析样本输出)的水果销售情况明细表。这份明细 表包括所有水果的产地、名称和销售数目的信息。水果先按产地分类,产地按字母顺序排列; 同一产地的水果按照名称排序,名称按字母顺序排序。

  • 参考代码

    #include <iostream>
    #include <map>
    #include <string>
    using namespace std;
    
    int main() {
        int N;
        map<string, map<string, int>> list;
        cin >> N;
        
        for (int i = 0; i < N; i++) {
            string name, place;
            int count;
            cin >> name >> place >> count;
            if (list.count(place) == 0) {
                map<string, int> good;
                good[name] = count;
                list[place] = good;
            }
            else {
                map<string, int> good = list[place];
                if (good.count(name) == 0) {
                    good[name] = count;
                    list[place] = good;
                }
                else {
                    good[name] += count;
                    list[place] = good;
                }
            }
        }
        
        for (map<string, map<string, int>> :: iterator it = list.begin(); it != list.end(); it++) {
            map<string, int> goods = it -> second;
            cout << it -> first << endl;
            for (map<string, int> :: iterator it2 = goods.begin(); it2 != goods.end(); it2++) {
                cout << "   |----" << it2 -> first << "(" << it2 -> second << ")" << endl;
            }
        }
        
        return 0;
    }
    

14. 任务系统(STL)

  • 描述

    蒜头君设计了一个任务系统。这个系统是为了定时提醒蒜头君去完成一些事情。

    系统大致如下,初始的时候,蒜头君可能会注册很多任务,每一个任务的注册如下:

    Register Q_num Period
    

    表示从系统启动开始,每过 Period 秒提醒蒜头君完成编号为 Qnum的任务。

    你能计算出蒜头君最先被提醒的 k 个任务吗?

  • 输入描述

    第一行输入n(0<n≤3000),k(0<k≤10000),其中 n 表示蒜头君注册的任务数量。

    接下来 n 行,每行输入一条注册命令,其中 30000<qnum≤3000,0≤Period≤3000。

  • 输出描述

    顺序输出 k 行,表示依次提醒的任务的编号。如果同一时间有多个任务,最先提醒编号小的任务。

  • 参考代码

    #include <iostream>
    #include <queue>
    using namespace std;
    
    struct Register {
        int num;
        int time;
        int period;
    };
    struct cmp {
        bool operator()(Register a, Register b) {
            if (a.period == b.period)
                return a.num > b.num;
            return a.period > b.period;
        }
    };
    
    int main() {
        int n, k;
        cin >> n >> k;
    
        priority_queue<Register, vector<Register>, cmp> list;
        for (int i = 0; i < n; i++) {
            string name;
            int num, period;
            cin >> name >> num >> period;
    
            Register reg;
            reg.num = num;
            reg.period = period;
            reg.time = period;
            list.push(reg);
        }
    
        for (int i = 0; i < k; i++) {
            cout << list.top().num << endl;
            Register reg = list.top();
            reg.period += reg.time;
            list.push(reg);
            list.pop();
        }
        return 0;
    }
    

15. n个最小和(STL)

  • 描述

    给出两个包含n个整数的数组A, B.分别在A, B 中任意出一个数并且相加,可以得到n^2个和。求这些和中最小的n个。

  • 输入描述

    输入第一行一个整数 n(1<=n<=50000)接下来一行输入数组A,用空格隔开。接下来一行输入数组B,用空格隔开。
    1<=Ai,Bi<=10^9

  • 输出描述

    从小到大输出最小的n个和,用空格隔开。

  • 参考代码

    #include <iostream>
    #include <vector>
    #include <queue>
    #include <functional>
    using namespace std;
    
    int main(){
        int n;
        cin >> n;
        vector<int> listA;
        vector<int> listB;
        priority_queue<int, vector<int>, greater<int>> list;
        
        for (int i = 0; i < n; i++) {
            int A;
            cin >> A;
            listA.push_back(A);
        }
        for (int i = 0; i < n; i++) {
            int B;
            cin >> B;
            listB.push_back(B);
        }
        
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                list.push(listA[i] + listB[j]);
            }
        }
        
        for (int i = 0; i < n; i++) {
    		cout << list.top() << " ";
            list.pop();
        }
        return 0;
    }
    

16. 得到整数 X(二进制枚举)

  • 描述

    小明有 n 个互不相同的正整数,现在他要从这 n个正整数之中无重复地选取任意个数,并仅通过加法凑出整数 X。求小明有多少种不同的方案来凑出整数 X。

  • 输入描述

    第一行,输入两个整数 n,X(1≤n≤20,1≤X≤2000)。
    接下来输入 n个整数,每个整数不超过 100。

  • 输出描述

    输出一个整数,表示能凑出 X 的方案数。

  • 参考代码

    #include <iostream>
    using namespace std;
    
    int main() {
        int n, X;
        int list[20];
        int count = 0;
        cin >> n >> X;
        
        for (int i = 0; i < n; i++) {
            cin >> list[i];
        }
        
        for (int i = 0; i < (1 << n); i++) {
            int sum = 0;
            for (int j = 0; j < n; j++) {
                if (i & (1 << j)) {
                    sum += list[j];
                }
            }
            if (sum == X) {
                count++;
            }
        }
        
        cout << count;
        return 0;
    }
    

17. islands 打炉石传说(二进制枚举)

  • 描述

    islands最近在完一款游戏“炉石传说”,又名“魔兽英雄传”。炉石传说是一款卡牌类对战的游 戏。游戏是2人对战,总的来说,里面的卡牌分成2类,一类是法术牌,另一类是随从牌(所谓随 从就是怪物)。为了简化问题,现在假设随从牌的作用是召唤一个具有一定攻击力的怪物,法术 牌的作用是给某个随从增加一定攻击力。随从牌和法术牌的使用都需要消耗一定的法力值。现在 islands有10点法力值,手上有n张牌(islands最多有10张牌,否者他将会被爆牌T_T),有些是 法术牌,有些是随从牌。islands现在是大劣势,他想要是利用这10点法力值使得召唤出来的所有 随从的攻击力总和最高(法力值可以不用完)。注意,任何法术牌都必须使用在某个召唤出来的 随从上,也就是如果islands没有召唤过随从,他将不能使用任何法术牌。告诉islands他能召唤 的随从的总攻击力最大是多少。

  • 输入描述

    每组数据首先输入一个 n(0≤n≤10,表示 islands 有 n 张牌。接下来n 行,每行输入3个整数cost(0≤cost≤10),d(0 或者 1),w(∣w∣≤1000)。其中 cost表示该牌的法力值消耗,如果d=0,表示该牌是攻击力为 w 的随从牌;如果 d=1,表示是能给一个随从增加w攻击的法术牌。

  • 输出描述

    输出一行表示答案。

  • 参考代码

    #include <iostream>
    using namespace std;
    
    int main() {
        int n;
        int list[10][3];
        int result = 0;
        cin >> n;
        
        for (int i = 0; i < n; i++) {
            cin >> list[i][0] >> list[i][1] >> list[i][2];
        }
        
        for (int i = 0; i < (1 << n); i++) {
            int sum = 0;
            int fali = 0;
            bool flag = false;
            for (int j = 0; j < n; j++) {
                if (i & (1 << j)) {
                    sum += list[j][2];
                    fali += list[j][0];
                    if (list[j][1] == 0) {
                        flag = true;
                    }
                }
            }
            if (sum > result && flag && fali <= 10) {
                result = sum;
            }
        }
        
        cout << result;
        return 0;
    }
    

18. 幼儿园买玩具(二进制枚举)

  • 描述

    幼儿园有n个小朋友,每个小朋友都有自己想玩的玩具。身为幼儿园园长的你决定给幼儿园买一 批玩具,由于经费有限,你只能买 m个玩具。已知玩具商店一共卖 k 种玩具,编号为 1,2,3,…k, 你让每个小朋友把想玩的玩具编号都写在了纸上。你希望满足尽可能多的小朋友的需求,请计算 出最多能满足多少个小朋友的玩具需求。

  • 输入描述

    第一行,输入三个整数n,m,k(1≤n≤100,1≤m≤15,1≤k≤15),中间用空格分开。接下来 n 行,第i+1(0≤i<n) 行的第一个数字 ai 代表第 i个小朋友想玩的玩具数量,接下来有 ai 个数字, 代表这ai个玩具的编号。

  • 输出描述

    输出一个整数,表示最多能满足多少小朋友的玩具需求。

  • 参考代码

    #include <iostream>
    using namespace std;
    
    int main() {
        int n, m, k;
        int result = 0;
        int list[101][16];
        cin >> n >> m >> k;
        
        for (int i = 0; i < n; i++) {
            cin >> list[i][0];
            for (int j = 1; j <= list[i][0]; j++) {
                cin >> list[i][j];
            }
        }
        
        for (int i = 0; i < (1 << k); i++) {
            int flag[16] = {0};
            int count = 0;
            int num = 0;
            for (int j = 0; j < k; j++) {
                if (i & (1 << j)) {
                    num++;
                    flag[j + 1] = 1;
                }
            }
            for (int x = 0; x < n; x++) {
                bool a = true;
                for (int y = 1; y <= list[x][0]; y++) {
                    if (flag[list[x][y]] == 0) {
                        a = false;
                    }
                }
                if (a)
                    count++;
            }
            if (count > result && num <= m) {
                result = count;
            }
        }
        
        cout << result;
        return 0;
    }
    

19. 约瑟夫环问题(循环链表)

  • 描述

    N个人围成一圈,从第一个人开始报数,数到M的人出圈;再由下一个人开始报数,数到M的人出圈;…输出依次出圈的人的编号。N,M由键盘输人。

  • 输入描述

    一行,n人,报到m出圈

  • 输出描述

    输出依次出圈的人的编号,用空格分隔

  • 参考代码

    #include <iostream>
    #include <list>
    using namespace std;
    
    int main() {
        int n, m;
        cin >> n >> m;
        
        list<int> list;
        int count = 1;
        for (int i = 1; i <= n; i++) {
            list.push_back(i);
        }
        while (list.size() != 1) {
            if (count == m) {
                cout << list.front() << " ";
                list.pop_front();
                count = 1;
                continue;
            }
            list.push_back(list.front());
            list.pop_front();
            count++;
        }
        cout << list.front();
        return 0;
    }
    

20. 网页跳转

  • 描述

    小明每天都在用一款浏览器软件。在这个浏览器中,一共三种操作:打开页面、 回退和前进。它们的功能如下:
    打开页面:在地址栏中输入网址,并跳转到网址对应的页面;
    回退:返回到上一次访问的页面;
    前进:返回到上次回退前的页面,如果上一次操作是打开页面,那么将无法 前进。
    现在,小明打开浏览器,进行了一系列操作,你需要输出他每次操作后所在页 面的网址。

  • 输入描述

    第一行输入一个整数n(0<n≤100000),表示小明的操作次数。接 下来一共n行,每行首先输入一个字符串,如果是VISIT,后面接着输入一个不 含有空格和换行的网址(网址长度小于100),表示小明在浏览器地址栏中输 入的网址;如果是BACK,表示小明点击了回退按钮;如果是FORWARD,表示小明点击了前进按钮。

  • 输出描述

    对于每次操作,如果小明能操作成功,输出小明操作之后的网址, 否则输出Ignore。假设小明输入的所有网址都是合法的。

  • 参考代码

    #include <iostream>
    #include <stack>
    using namespace std;
    
    int main() {
        int n;
        stack<string> bak;
        stack<string> fwd;
        cin >> n;
    
        for (int i = 0; i < n; i++) {
            string tmp;
            cin >> tmp;
            if (tmp == "VISIT") {
                cin >> tmp;
                bak.push(tmp);
                cout << tmp << endl;
                while (!fwd.empty()) {
                    fwd.pop();
                }
            }
            if (tmp == "BACK") {
                if (bak.size() <= 1)
                    cout << "Ignore" << endl;
                else {
                    fwd.push(bak.top());
                    bak.pop();
                    cout << bak.top() << endl;
                }
            }
            if (tmp == "FORWARD") {
                if (fwd.empty())
                    cout << "Ignore" << endl;
                else {
                    cout << fwd.top() << endl;
                    bak.push(fwd.top());
                    fwd.pop();
                }
            }
        }
        return 0;
    }
    

21. 等边三角形

  • 描述

    小明手上有一些小木棍,它们长短不一,小明想用这些木棍拼出一个等边三角形,并且每根木棍都要用到。例如,小明手上有长度为 1,2,3,3 的 4 根木棍,他可以让长度为 1,2 的木棍组成一条边,另外 2 根分别组成另 2 条边,拼成一个边长为 3 的等边三角形。小明希望你提前告诉他能不能拼出来,免得白费功夫。

  • 输入描述

    首先输入一个整数n(3≤n≤20),表示木棍数量,接下来输入n根木棍的长度 pi (1≤pi≤10000)。

  • 输出描述

    如果小明能拼出等边三角形,输出"yes",否则输出"no"。

  • 参考代码

    #include<iostream>
    using namespace std;
    
    int length[20];
    int n, sum;
    bool flag = false;
    int path[20];
    
    void solve(int edges, int len, int start) {
    	if (flag) {
    		return;
    	}
    	if (edges == 3) {
    		flag = true;
    		return;
    	}
    	if (len == sum / 3) {
    		solve(edges + 1, 0, 0);
    	}
    	for (int i = start; i < n; i++) {
            if (path[i] == 0) {
                path[i] = 1;
                solve(edges, len + length[i], i + 1);
                path[i] = 0;
            }
    	}
    }
    
    int main() {
    	cin >> n;
    	for (int i = 0; i < n; i++) {
    		cin >> length[i];
    		sum += length[i];
    	}
    
    	if (sum % 3 != 0) {
    		cout << "no";
    		return 0;
    	}
    	solve(0, 0, 0);
    	if (flag) {
    		cout << "yes";
    	}
    	else {
    		cout << "no";
    	}
    	return 0;
    }
    

22. 小明吃桃

  • 描述

    小明买了一堆桃子不知道个数,第一天吃了一半的桃子,还不过瘾,又多吃了一个。以后他每天吃剩下的桃子的一半还多一个,到 n 天只剩下一个桃子了。蒜头君想知道一开始买了多少桃子。

  • 输入描述

    输入一个整数n(2≤n≤60),代表第 n 天剩了一个桃子。

  • 输出描述

    输出买的桃子的数量。

  • 参考代码

    #include <iostream>
    using namespace std;
    
    long long func(long long n) {
        if (n == 1)
            return 1;
        return 2 * func(n - 1) + 2;
    }
    
    int main() {
        long long n;
        cin >> n;
        cout << func(n);
        return 0;
    }
    

23. 汉诺塔

  • 描述

    汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着 6464 片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。

    现在小明开始玩汉诺塔游戏, 他放了 n 片黄金圆盘在第一根柱子上,从上到下依次编号为 1−n, 1 号圆盘最小,n 号圆盘最大。小明移动第 i 号圆盘的时候需要花费 i 点体力。现在小明想把圆盘全部移动到底 2 根柱子上,移动过程中蒜头君必须准守游戏规则。

    现在小明想知道他完成游戏的最小移动次数和最少消耗的体力。

  • 输入描述

    输入一个正整数 n(1≤n≤60) 表示黄金圆盘的个数

  • 输出描述

    一行输出 2 个数,表示最小移动次数和最小消耗的体力,中间用一个空格隔开。

  • 参考代码

    #include<iostream>
    using namespace std;
    
    long long moveCount(int n) {
    	if (n == 1) {
    		return 1;
    	}
    	return 1 + 2 * moveCount(n - 1);
    }
    
    long long strengthCost(int n) {
    	if (n == 1) {
    		return 1;
    	}
    	return n + 2 * strengthCost(n - 1);
    }
    
    int main() {
    	int n;
    	cin >> n;
    	cout << moveCount(n) << " " << strengthCost(n);
    	return 0;
    }
    

24. 走迷宫

  • 描述

    给一个 n 行 m 列的 2 维的迷宫,‘S’表示迷宫的起点,‘T’表示迷宫的终点,’#‘表示不能通过的点,’.’ 表示可以通过的点。你需要从’S’出发走到’T’,每次只能上下左右走动,并且只能进入能通过的点,每个点只能通过一次。现在要求你求出有多少种通过迷宫的的方案。

  • 输入描述

    第一行输入 n, m (1≤n,m≤10) 表示迷宫大小。

    接下来输入 n 行字符串表示迷宫。

  • 输出描述

    输入通过迷宫的方法数。

  • 参考代码

    #include<iostream>
    using namespace std;
    
    int px[4] = { -1, 1, 0, 0 };
    int py[4] = { 0, 0, -1, 1 };
    int vst[10][10] = { 0 };
    char map[10][10];
    int m, n;
    int wayCount = 0;
    
    void maze(int x, int y) {
    	vst[x][y] = 1;
    	for (int i = 0; i < 4; i++) {
    		int nx = x + px[i];
    		int ny = y + py[i];
    
    		if (nx >= 0 && nx < n && ny >= 0 && ny < m && vst[nx][ny] == 0 && map[nx][ny] != '#') {
    			if (map[nx][ny] == 'T') {
    				wayCount++;
    			}
    			else {
    				maze(nx, ny);
    			}
    		}
    	}
    	vst[x][y] = 0;
    }
    
    int main() {
        int x, y;
        cin >> n >> m;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                cin >> map[i][j];
                if (map[i][j] == 'S') {
                    x = i;
                    y = j;
                }
            }
        }
    
        maze(x, y);
        cout << wayCount;
        return 0;
    }
    

25. 踏青

  • 描述

    小明和他的朋友周末相约去召唤师峡谷踏青。他们发现召唤师峡谷的地图是由一块一块格子组成的,有的格子上是草丛,有的是空地。草丛通过上下左右 4 个方向扩展其他草丛形成一片草地,任何一片草地中的格子都是草丛,并且所有格子之间都能通过上下左右连通。如果用’#‘代表草丛,’.'代表空地,下面的峡谷中有 2 片草地。

    ##..
    ..##
    

    处在同一个草地的 2 个人可以相互看到,空地看不到草地里面的人。他们发现有一个朋友不见了,现在需要分头去找,每个人负责一片草地,蒜头君想知道他们至少需要多少人。

  • 输入描述

    第一行输入 n, m (1≤n,m≤100) 表示峡谷大小

    接下来输入 n 行字符串表示峡谷的地形

  • 输出描述

    输出至少需要多少人

  • 参考代码

    #include<iostream>
    using namespace std;
    
    int px[4] = {-1, 1, 0, 0};
    int py[4] = {0, 0, -1, 1};
    char map[100][100];
    int vst[100][100] = { 0 };
    int n, m;
    
    void grass(int x, int y) {
    	vst[x][y] = 1;
    	for (int i = 0; i < 4; i++) {
    		int nx = x + px[i];
    		int ny = y + py[i];
    
    		if (nx >= 0 && nx < n && ny >= 0 && ny < m && vst[nx][ny] == 0 && map[nx][ny] == '#') {
    			grass(nx, ny);
    		}
    	}
    }
    
    int main() {
        cin >> n >> m;
        int count = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                cin >> map[i][j];
            }
        }
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (map[i][j] == '#' && vst[i][j] == 0) {
                    grass(i, j);
                    count++;
                }
            }
        }
        cout << count;
        return 0;
    }
    

26. 买书

  • 描述

    小明去书店买书,他有 m 元钱,书店里面有 n 本书,每本书的价格为 pi元。蒜头君很爱学习,想把身上钱都用来买书,并且刚好买 k 本书。请帮小明计算他是否能刚好用 m 元买 k 本书。

  • 输入描述

    第一行输入 3 个整数 m(1≤m≤100000000),n(1≤n≤30),k(1≤k≤min(8,n))
    接下来一行输入 n 个整数,表示每本书的价格 pi(1≤pi≤100000000)。

  • 输出描述

    如果蒜头君能 刚好 用 m 元买 k 本书,输入一行"Yes", 否则输出"No"。

  • 参考代码

    #include <iostream>
    using namespace std;
    
    int main() {
        int price[30];
        int m, n, k;
        bool result = false;
        cin >> m >> n >> k;
        for (int i = 0; i < n ; i++)
            cin >> price[i];
        
        for (int i = 0; i < (1 << n); i++) {
            int num = 0;
            int sum = 0;
            for (int j = 0; j < n; j++) {
                if ((1 << j) & i) {
                    num++;
                    sum += price[j];
                    if (num > k || sum > m)
                        break;
                }
            }
            if (sum == m && num == k) {
                result = true;
                break;
            }
        }
        if (result)
            cout << "Yes";
        else
            cout << "No";
        return 0;
    }
    

27. 正方形

  • 描述

    小明手上有一些小木棍,它们长短不一,小明想用这些木棍拼出一个正方形,并且每根木棍都要用到。 例如,小明手上有长度为1,2,3,3,3的5根木棍,他可以让长度为1,2 的木棍组成一条边,另外3根分别组成 另3条边,拼成一个边长为3的正方形。小明希望你提前告诉他能不能拼出来,免得白费功夫。

  • 输入描述

    首先输入一个整数n(4<=n<=20),表示木棍数量,接下来输入n根木棍的长度pi (1<=p<=10000)

  • 输出描述

    如果小明能拼出正方形,输出"yes",否则输出"no"。

  • 参考代码

    #include<iostream>
    using namespace std;
    
    int length[20];
    int n, sum;
    bool flag = false;
    int path[20];
    
    void solve(int edges, int len, int start) {
    	if (flag) {
    		return;
    	}
    	if (edges == 4) {
    		flag = true;
    		return;
    	}
    	if (len == sum / 4) {
    		solve(edges + 1, 0, 0);
    	}
    	for (int i = start; i < n; i++) {
            if (path[i] == 0) {
                path[i] = 1;
                solve(edges, len + length[i], i + 1);
                path[i] = 0;
            }
    	}
    }
    
    int main() {
    	cin >> n;
    	for (int i = 0; i < n; i++) {
    		cin >> length[i];
    		sum += length[i];
    	}
    
    	if (sum % 4 != 0) {
    		cout << "no";
    		return 0;
    	}
    	solve(0, 0, 0);
    	if (flag) {
    		cout << "yes";
    	}
    	else {
    		cout << "no";
    	}
    	return 0;
    }
    

28. 红与黑

  • 描述

    有一间长方形的房子,地上铺了红色、黑色两种颜色的正方形瓷砖。你站在其中一块黑色的瓷砖上,只能向相邻的黑色瓷砖移动。请写一个程序,计算你总共能够到达多少块黑色的瓷砖。

  • 输入描述

    包括多个数据集合。每个数据集合的第一行是两个整数W和H,分别表示x方向和y方向瓷砖的数量。W和H都不超过20。在接下来的H行中,每行包括W个字符。每个字符表示一块瓷砖的颜色,规则如下:

    1)‘.’:黑色的瓷砖;

    2)‘#’:白色的瓷砖;

    3)‘@’:黑色的瓷砖,并且你站在这块瓷砖上。该字符在每个数据集合中唯一出现一次。

    当在一行中读入的是两个零时,表示输入结束。

  • 输出描述

    对每个数据集合,分别输出一行,显示你从初始位置出发能到达的瓷砖数(记数时包括初始位置的瓷砖)。

  • 参考代码

    #include <iostream>
    #include <cstring>
    using namespace std;
    
    char map[20][20];
    int px[4] = { 0, 0, -1, 1 };
    int py[4] = { -1, 1, 0, 0 };
    int vst[20][20] = { 0 };
    int n, m, blackCount;
    
    void black(int x, int y) {
        vst[x][y] = 1;
        blackCount++;
        for (int i = 0; i < 4; i++) {
            int nx = x + px[i];
            int ny = y + py[i];
            if (vst[nx][ny] == 0 && nx >= 0 && nx < n && ny >= 0 && ny < m && map[nx][ny] == '.') {
                black(nx, ny);
            }
        }
    }
    
    int main() {
        int x, y;
        cin >> m >> n;
        while (n != 0 && m != 0) {
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < m; j++) {
                    cin >> map[i][j];
                    if (map[i][j] == '@') {
                        x = i;
                        y = j;
                    }
                }
            }
            blackCount = 0;
            black(x, y);
            cout << blackCount << endl;
            cin >> m >> n;
        }
        return 0;
    }
    

29. Intervals

  • 描述

    There is given the series of n closed intervals [ai; bi], where i=1,2,…,n. The sum of those intervals may be represented as a sum of closed pairwise non−intersecting intervals. The task is to find such representation with the minimal number of intervals. The intervals of this representation should be written in the output file in acceding order. We say that the intervals [a; b] and [c; d] are in ascending order if, and only if a <= b < c <= d.
    Task
    Write a program which:
    reads from the std input the description of the series of intervals,
    computes pairwise non−intersecting intervals satisfying the conditions given above,
    writes the computed intervals in ascending order into std output

  • 输入描述

    In the first line of input there is one integer n, 3 <= n <= 50000. This is the number of intervals. In the (i+1)−st line, 1 <= i <= n, there is a description of the interval [ai; bi] in the form of two integers ai and bi separated by a single space, which are respectively the beginning and the end of the interval,1 <= ai <= bi <= 1000000.

  • 输出描述

    The output should contain descriptions of all computed pairwise non−intersecting intervals. In each line should be written a description of one interval. It should be composed of two integers, separated by a single space, the beginning and the end of the interval respectively. The intervals should be written into the output in ascending order.

  • 参考代码

    #include<iostream>
    #include<vector>
    #include<algorithm>
    using namespace std;
    
    struct Interval {
    	int start;
    	int end;
    };
    
    bool cmp(Interval a, Interval b) {
    	return a.start < b.start;
    }
    
    int main() {
    	int n;
    	int count = 0;
    	cin >> n;
    	vector<Interval> intervals(n);
    	vector<Interval> result;
    
    	for (int i = 0; i < n; i++) {
    		cin >> intervals[i].start >> intervals[i].end;
    	}
    	sort(intervals.begin(), intervals.end(), cmp);
    
    	result.push_back(intervals[0]);	
    	for (int i = 1; i < n; i++) {
    		if (intervals[i].start <= result[count].end) {
    			result[count].end = max(intervals[i].end, result[count].end);
    		}
    		else {
    			result.push_back(intervals[i]);
    			count++;
    		}
    	}
    
    	for (int i = 0; i <= count; i++) {
    		cout << result[i].start << " " << result[i].end << endl;
    	}
    	return 0;
    }
    

30. 金字塔数独

  • 描述

    小明天资聪颖,酷爱数学,尤其擅长做数独游戏。不过普通的数独游戏已经满足不了小明了,于是他发明了一种“金字塔数独”:
    下图即为金字塔数独。和普通数独一样,在9×9的大九宫格中有9个3×3的小九宫格(用粗黑色线隔开的)。要求每个格子上都有一个 1 到 9 的数字,每个数字在每个小九宫格内不能重复出现,每个数字在每行、每列也不能重复出现。
    但金字塔数独的每一个格子都有一个分值,类似金字塔的俯视图。如图所示,白色格子为6分,蓝色格子为 7分,绿色格子为 8 分,紫色格子为 9 分,红色格子为 10 分。颜色相同的格子分值一样,离中心越近则分值越高。
    金字塔数独的总分等于每个格子上的数字和对应的分值乘积之和。现在小明给定金字塔数独的若干数字,请问如何填写,可以使得金字塔数独的总分最高。

    在这里插入图片描述

  • 输入描述

    输入一共9行。每行输入 9 个整数(每个数都在 0—9的范围内),每两个整数之间用一个空格隔开,“0”表示该格子为空。

  • 输出描述

    输出为一行,输出一个整数,代表金字塔数独的最高总分。
    如果数独无解,则输出-1。

  • 参考代码

    #include<iostream>
    using namespace std;
    
    const int N = 9;
    int grid[N][N];
    int scores[N][N] = {
        {6, 6, 6, 6, 6, 6, 6, 6, 6},
        {6, 7, 7, 7, 7, 7, 7, 7, 6},
        {6, 7, 8, 8, 8, 8, 8, 7, 6},
        {6, 7, 8, 9, 9, 9, 8, 7, 6},
        {6, 7, 8, 9, 10, 9, 8, 7, 6},
        {6, 7, 8, 9, 9, 9, 8, 7, 6},
        {6, 7, 8, 8, 8, 8, 8, 7, 6},
        {6, 7, 7, 7, 7, 7, 7, 7, 6},
        {6, 6, 6, 6, 6, 6, 6, 6, 6}
    };
    int maxScore = -1;
    
    bool isValid(int x, int y, int num) {
        for (int i = 0; i < N; i++) {
            if (grid[i][y] == num || grid[x][i] == num) {
                return false;
            }
        }
    
        int rowStart = 3 * (x / 3);
        int colStart = 3 * (y / 3);
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if (grid[rowStart + i][colStart + j] == num) {
                    return false;
                }
            }
        }
    
        return true;
    }
    
    void solve(int x, int y, int totalScore) {
        if (x == N) {
            maxScore = max(maxScore, totalScore);
            return;
        }
    
        int nx = y == N - 1 ? x + 1 : x;
        int ny = y == N - 1 ? 0 : y + 1;
        if (grid[x][y] == 0) {
            for (int i = 1; i < 10; i++) {
                if (!isValid(x, y, i)) {
                    continue;
                }
                grid[x][y] = i;
                solve(nx, ny, totalScore + i * scores[x][y]);
            }
            grid[x][y] = 0;
        }
        else {
            solve(nx, ny, totalScore + grid[x][y] * scores[x][y]);
        }
    }
    
    int main() {
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                cin >> grid[i][j];
            }
        }
        solve(0, 0, 0);
        if (maxScore == 0) {
            cout << -1 << endl;
        }
        else {
            cout << maxScore << endl;
        }
        return 0;
    }
    

31. 一维坐标的移动

  • 描述

    在一个长度为 n的坐标轴上,小明想从 A 点 移动到 B 点。他的移动规则如下:
    向前一步,坐标增加 1。
    向后一步,坐标减少 1
    跳跃一步,使得坐标乘 2
    小明不能移动到坐标小于 0 或大于 n 的位置。小明想知道从 A 点移动到 B 点的最少 步数是多少,你能帮他计算出来么?

  • 输入描述

    第一行输入三个整数 n,A,B,分别代表坐标轴长度,起始点坐标,终点坐标。
    (0≤A,B≤n≤5000)

  • 输出描述

    输出一个整数占一行,代表小明要走的最少步数。

  • 参考代码

    #include <iostream>
    #include <queue>
    #include <vector>
    using namespace std;
    
    int minSteps(int n, int start, int end) {
        if (start == end) 
            return 0;
    
        vector<bool> visited(n + 1, false);
        queue<pair<int, int>> q;
        q.push({start, 0});
    
        while (!q.empty()) {
            int pos = q.front().first;
            int steps = q.front().second;
            q.pop();
    
            if (pos == end) return steps;
    
            if (pos + 1 <= n && !visited[pos + 1]) {
                q.push({pos + 1, steps + 1});
                visited[pos + 1] = true;
            }
            if (pos - 1 >= 0 && !visited[pos - 1]) {
                q.push({pos - 1, steps + 1});
                visited[pos - 1] = true;
            }
            if (pos * 2 <= n && !visited[pos * 2]) {
                q.push({pos * 2, steps + 1});
                visited[pos * 2] = true;
            }
        }
    
        return -1;
    }
    
    int main() {
        int n, start, end;
        cin >> n >> start >> end;
    
        int result = minSteps(n, start, end);
        cout << result << endl;
    
        return 0;
    }
    

32. 多机调度问题

  • 描述

    设有n个独立的作业{1, 2, …, n},由m台相同的机器{M1, M2, …, Mm}进行加工处理,作业i所需的处理时间为ti(1≤i≤n),每个作业均可在任何一台机器上加工处理,但不可间断、拆分。多机调度问题要求给出一种作业调度方案,使所给的n个作业在尽可能短的时间内由m台机器加工处理完成。

  • 输入描述

    两行,第一行为两个整数n,m。(1<=m<=50,m<=n<=100)。第二行n个整数,代表n个任务的时间。

  • 输出描述

    一个整数,最小执行时间。

  • 参考代码

    #include <iostream>
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    int main() {
        int n, m;
        vector<int> task;
        cin >> n >> m;
        vector<int> mac(m);
    
        for (int i = 0; i < n; i++) {
            int tmp;
            cin >> tmp;
            taxt.push_back(tmp);
        }
        sort(taxt.begin(), taxt.end(), greater<int>());
        if (n <= m) {
            cout << taxt[n - 1];
            return 0;
        }
        else {
            for (int j = 0; j < m; j++) {
                mac[j] = taxt[j];
            }
            for (int j = m; j < n; j++) {
                sort(mac.begin(), mac.end());
                mac[0] += taxt[j];
            }
        }
    
        sort(mac.begin(), mac.end(), greater<int>());
        cout << mac[0];
        return 0;
    }
    

33. 迷瘴

  • 描述

    小明正在玩游戏,他控制的角色正面临着幽谷的考验——
    幽谷周围瘴气弥漫,静的可怕,隐约可见地上堆满了骷髅。由于此处长年不见天日,导致空气中布满了毒素,一旦吸入体内,便会全身溃烂而死。
    幸好小明早有防备,提前备好了解药材料(各种浓度的万能药水)。现在只需按照配置成不同比例的浓度。
    现已知小明随身携带有n种浓度的万能药水,体积V都相同,浓度则分别为Pi%。并且知道,针对当时幽谷的瘴气情况,只需选择部分或者全部的万能药水,然后配置出浓度不大于 W%的药水即可解毒。
    现在的问题是:如何配置此药,能得到最大体积的当前可用的解药呢?
    特别说明:由于幽谷内设备的限制,只允许把一种已有的药全部混入另一种之中(即:不能出现对一种药只取它的一部分这样的操作)。

  • 输入描述

    每组测试数据包含2行,首先一行给出三个正整数n,V,W(1<=n,V,W<=100);
    接着一行是n个整数,表示n种药水的浓度Pi%(1<=Pi<=100)。

  • 输出描述

    对于每组测试数据,请输出一个整数和一个浮点数;
    其中整数表示解药的最大体积,浮点数表示解药的浓度(四舍五入保留2位小数);
    如果不能配出满足要求的的解药,则请输出0 0.00。

  • 参考代码

    #include<iostream>
    #include<vector>
    #include<algorithm>
    using namespace std;
    
    int main() {
        int n, V, W;
        int sum = 0;
        int count = 0;
        double result;
        vector<int> med;
        cin >> n >> V >> W;
    
        for (int i = 0; i < n; i++) {
            int tmp;
            cin >> tmp;
            med.push_back(tmp);
        }
        sort(med.begin(), med.end());
    
        if (med[0] > W) {
            cout << "0 0.00";
            return 0;
        }
        for (int i = 0; i < n; i++) {
            sum += med[i];
            count++;
            double tmp = (double)sum / count;
            if (tmp > W)
                break;
            result = tmp;
        }
    
        printf("%d %.2lf", count * V, result);
        return 0;
    }
    

34. 活动安排

  • 描述

    有很多电视节目,给出它们的起止时间。有些节目时间冲突。问能完整看完的电视节目最多有多少

  • 输入描述

    第一行只有一个整数n(n<=100),表示你喜欢看的节目的总数。
    然后是n行数据,每行包括两个数据,分别表示第i个节目的开始和结束时间。每个时间都用一个正整数表示。

  • 输出描述

    输出能完整看到的最多电视节目的个数。

  • 参考代码

    #include<iostream>
    #include<vector>
    #include<algorithm>
    using namespace std;
    
    struct Program {
    	int start;
    	int end;
    };
    
    bool cmp(Program a, Program b) {
    	return a.end < b.end;
    }
    
    int main() {
    	int n;
    	cin >> n;
    	vector<Program> programs(n);
    
    	for (int i = 0; i < n; i++) {
    		cin >> programs[i].start >> programs[i].end;
    	}
    	sort(programs.begin(), programs.end(), cmp);
    
    	int count = 1;
    	int tmp = programs[0].end;
    	for (int i = 1; i < n; i++) {
    		if (programs[i].start >= tmp) {
    			tmp = programs[i].end;
    			count++;
    		}
    	}
    
    	cout << count;
    	return 0;
    }
    

35. 逃跑

  • 描述

    小明被困在了一个 n+1 行 m+1 列的迷宫当中,小明所在位置为左上角的(0,0),他需要逃跑到位于右下角(n,m)的出口位置。在逃跑的过程中,小明只可以向东南西北四个方向移动,当然也可以选择停留在某一位置,他每移动一个单位距离需要 1 秒的时间,小明初始时刻的能量为d,小明在迷宫当中每过 1 秒需要消耗 1单位能量。在迷宫中有 k 个士兵,他们会朝着某一方向周期性地射击,子弹只会在整点位置射中小明。当小明被子弹射中、和士兵相遇或者能量消耗完时,他将死去。求小明逃离迷宫的最短时间。

  • 输入描述

    第一行包含四个整数n,m,k,d(2<=n,m<=100;0<=k<=100;m+n<=d<=1000)。 接下来k行每行包含一个字符c和四个整数t,v,x,y(t,v<=100;0<=x<=n;0<=y<=m),其中c表示每个士兵的射击方向,使用N、S、W、E表示上下左右四个方向,射击的周期为t,子弹的速度为v,士兵所在位置为(x,y)。

  • 输出描述

    输出一个整数,表示小明逃跑的最短时间,如果小明不能成功的逃离迷宫,输出Bad luck!

  • 参考代码

    #include<iostream>
    #include<vector>
    #include<queue>
    #include<bitset>
    using namespace std;
    
    const int N = 110;
    int dx[] = { 0, 0, 1, -1, 0 };
    int dy[] = { 1, -1, 0, 0, 0 };
    int pretime = -1;
    int n, m, k, d;
    bitset<N> vis[N];
    bitset<N> sold[N];
    bitset<N> go[N][N];
    
    struct Role {
    	int x; 
    	int y; 
    	int time; 
    	int d;
    };
    
    struct Soldier {
    	char c;
    	int t; 
    	int v; 
    	int x; 
    	int y;
    }soldiers[N];
    
    struct Bullet {
    	int x; 
    	int y; 
    	int v; 
    	char c;
    };
    
    queue<Role> roles;
    vector<Bullet> bullets;
    
    bool isValid(int x, int y) {
    	return x >= 0 && x <= n && y >= 0 && y <= m;
    }
    
    void bulletUpdate(Bullet& bullet) {
    	switch (bullet.c) {
    		case 'N':
    			bullet.x -= bullet.v; 
    			break;
    		case 'W':
    			bullet.y -= bullet.v; 
    			break;
    		case 'E':
    			bullet.y += bullet.v; 
    			break;
    		case 'S':
    			bullet.x += bullet.v; 
    			break;
    	}
    }
    
    void update(int time) {
    	if (pretime == time) {
    		return;
    	}
    	else {
    		pretime = time;
    		for (Bullet bullet : bullets) {
    			if (!isValid(bullet.x, bullet.y)) {
    				continue;
    			}
    			vis[bullet.x][bullet.y] = 0;
    			bulletUpdate(bullet);
    			if (!isValid(bullet.x, bullet.y)) {
    				continue;
    			}
    			vis[bullet.x][bullet.y] = 1;
    		}
    		for (int i = 1; i <= k; i++)
    		{
    			if (time % soldiers[i].t == 0) {
    				bullets.push_back({ soldiers[i].x, soldiers[i].y, soldiers[i].v, soldiers[i].c });
    				vis[soldiers[i].x][soldiers[i].y] = 1;
    			}
    		}
    
    	}
    }
    
    
    void bfs(int x, int y, int time, int d) {
    	bool flag = false;
    	roles.push({ x, y, time, d });
    	go[x][y] = true;
    
    	while (roles.size()) {
    		Role role = roles.front();
    		roles.pop();
    
    		if (role.d < m + n - role.x - role.y) {
    			continue;
    		}
    		if (go[n][m][role.time] == 1) {
    			cout << roles.back().time;
    			flag = true;
    			return;
    		}
    		for (int i = 0; i < 5; i++) {
    			int tx = role.x + dx[i];
    			int ty = role.y + dy[i];
    			update(role.time + 1);
    			if ((!isValid(tx, ty)) || vis[tx][ty] == 1 || go[tx][ty][role.time] || sold[tx][ty] == 1) {
    				continue;
    			}
    			if (!vis[tx][ty]) {
    				go[tx][ty][role.time] = 1;
    			}
    			roles.push({ tx, ty, role.time + 1, role.d - 1 });
    		}
    	}
    
    	if (!flag) {
    		cout << "Bad luck!";
    	}
    }
    
    void solve() {
    	cin >> n >> m >> k >> d;
    	for (int i = 1; i <= k; i++) {
    		char c;
    		int t, v, x, y;
    		cin >> c >> t >> v >> x >> y;
    		soldiers[i] = { c, t, v, x, y };
    		sold[x][y] = 1;
    	}
    	for (int i = 1; i <= k; i++) {
    		bullets.push_back({ soldiers[i].x, soldiers[i].y, soldiers[i].v, soldiers[i].c });
    		vis[soldiers[i].x][soldiers[i].y] = 1;
    	}
    
    	bfs(0, 0, 0, d);
    }
    
    int main() {
    	int q = 1;
    	for (int i = 0; i < q; i++) {
    		solve();
    	}
    	return 0;
    }
    

36. 小明回家

  • 描述

    小明要回家,但是他家的钥匙在他的朋友花椰妹手里,他要先从花椰妹手里取得钥匙才能回到家。花椰妹告诉他“你家的钥匙被我复制了很多个,分别放在不同的地方。”
    小明希望能尽快回到家中,他需要首先取得任意一把钥匙,请你帮他计算出回家所需要的最短路程。
    小明生活的城市可以看做是一个 nxm 的网格,其中有道路有障碍,钥匙和家所在的地方可以看做是道路,可以通过。小明可以在城市中沿着上下左右 4 个方向移动,移动一个格子算做走一步。

  • 输入描述

    第一行有两个整数 n, m。城市的地图是 n 行 m 列。(1<n,m<2000)。接下来的 n 行,每行 m 个字符,代表城市的地图。’.‘代表道路,’#'代表障碍物,'S’代表小明所在的位置,'T’代表小明家的位置,'P’代表钥匙的位置。除了障碍物以外,别的地方都可以通过。(题目保证小明至少有一条路径可以顺利拿到钥匙并且回家)

  • 输出描述

    输出小明回家要走的最少步数,占一行。

  • 参考代码

    #include<iostream>
    #include<vector>
    #include<queue>
    using namespace std;
    
    struct Point {
    	int x;
    	int y;
    	int step;
    };
    
    char map[2000][2000];
    int px[4] = {-1, 1, 0, 0};
    int py[4] = {0, 0, -1, 1};
    int n, m;
    
    int stepCount(Point start, Point end) {
    	int vst[2000][2000] = { 0 };
    	queue<Point> points;
    	points.push(start);
    	vst[start.x][start.y] = 1;
    
    	while (!points.empty()) {
    		Point tmp = points.front();
    		int x = tmp.x;
    		int y = tmp.y;
    		int step = tmp.step;
    		points.pop();
    
    		if (x == end.x && y == end.y) {
    			return step;
    		}
    
    		for (int i = 0; i < 4; i++) {
    			int nx = x + px[i];
    			int ny = y + py[i];
    
    			if (nx >= 0 && nx < n && ny >= 0 && ny < m && vst[nx][ny] == 0 && map[nx][ny] != '#') {
    				Point next;
    				next.x = nx;
    				next.y = ny;
    				next.step = step + 1;
    				points.push(next);
    				vst[nx][ny] = 1;
    			}
    		}
    	}
    
    	return -1;
    }
    
    int main() {
    	Point start, key, end;
    	vector<Point> keys;
    	int keyCount = 0;
    	int minStep = -1;
    	cin >> n >> m;
    	for (int i = 0; i < n; i++) {
    		for (int j = 0; j < m; j++) {
    			cin >> map[i][j];
    			if (map[i][j] == 'S') {
    				start.x = i;
    				start.y = j;
    				start.step = 0;
    			}
    			if (map[i][j] == 'P') {
    				key.x = i;
    				key.y = j;
    				keyCount++;
    				keys.push_back(key);
    			}
    			if (map[i][j] == 'T') {
    				end.x = i;
    				end.y = j;
    			}
    		}
    	}
    
    	for (int i = 0; i < keyCount; i++) {
    		if (stepCount(start, keys[i]) == -1)
    			continue;
    		keys[i].step = stepCount(start, keys[i]);
    		if (stepCount(keys[i], end) == -1)
    			continue;
    		minStep = minStep == -1 ? stepCount(keys[i], end) : min(stepCount(keys[i], end), minStep);
    	}
    	cout << minStep;
    	return 0;
    }
    

37. 一元三次方程求解

  • 描述

    有形如:a**x3+b**x2+c**x1+d**x0=0 这样的一个一元三次方程。给出该方程中各项的系数(a,b,c,d 均为实数),并约定该方程存在三个不同实根(根的范围在 −100 至 100 之间),且根与根之差的绝对值 ≥1。要求由小到大依次在同一行输出这三个实根(根与根之间留有空格),并精确到小数点后 2 位。

    提示:记方程 f(x)=0,若存在 2 个数 x1 和 x2,且 x1<x2,f(x1)×f(x2)<0,则在 (x1,x2) 之间一定有一个根。

  • 输入描述

    一行,4 个实数 A,B,C,D

  • 输出描述

    一行,3 个实根,并精确到小数点后 2 位。

  • 参考代码

    #include <iostream>
    #include <cstdio>
    using namespace std;
    
    double A, B, C, D;
    
    double f(double x) {
        return A*x*x*x + B*x*x + C*x + D;
    }
    
    int main() {
        cin >> A >> B >> C >> D;
        for (int i = -10000; i <= 10000; i++) {
            if (f((i - 0.5) / 100) * f((i + 0.5) / 100) < 0 || f((i - 0.5) / 100) == 0)
                printf("%.2lf ", i / 100.0);
        }
        return 0;
    }
    

38. 循环比赛日程表

  • 描述

    设有N个选手进行循环比赛,其中N=2的M次方,要求每名选手要与其他N-1名选手都赛一次,每名选手每天比赛一次,循环赛共进行N-1天,要求每天没有选手轮空。

  • 输入描述

    输入:M。

  • 输出描述

    输出:表格形式的比赛安排表。一行各数据宽度为3。

  • 参考代码

    #include<iostream>
    #include<vector>
    using namespace std;
    
    int main() {
    	int M;
    	int half = 1;
    	cin >> M;
    	vector<vector<int>> table(1 << M, vector<int>(1 << M, 0));
    	table[0][0] = 1;
    
    	for (int k = 0; k < M; k++) {
    		for (int i = 0; i < (1 << k); i++) {
    			for (int j = 0; j < (1 << k); j++) {
    				table[i][j + half] = table[i][j] + half;
    				table[i + half][j] = table[i][j] + half;
    				table[i + half][j + half] = table[i][j];
    			}
    		}
    		half *= 2;
    	}
    
    	for (int i = 0; i < (1 << M); i++) {
    		for (int j = 0; j < (1 << M); j++) {
    			cout << "  " << table[i][j];
    		}
    		cout << endl;
    	}
    
    	return 0;
    }
    

39. 逆序对

  • 描述

    ZZR 有一个序列 a1,a2,⋯,a**n,他允许最多进行 k 次操作,每次操作交换两个相邻元素。

    求经过变换后的序列中最少还有多少逆序对。

    逆序对指的是二元组 (i,j),其满足 i<ja**i>a**j

  • 输入描述

    第一行包含两个数 n,k,表示序列长度和交换两个相邻元素的次数上限。

    第二行有 n 个数,表示序列 a1,a2,⋯,a**n

  • 输出描述

    一个数表示最少的逆序对的数量。

  • 参考代码

    #include<iostream>
    using namespace std;
    
    int nums[100000];
    long long reverseCount = 0;
    
    void merge(int sta, int mid, int end) {
    	int tmp[100000];
    	int i = sta;
    	int j = mid + 1;
    	int k = sta;
    
    	while (i <= mid && j <= end) {
    		if (nums[i] > nums[j]) {
    			reverseCount += mid + 1 - i;
    			tmp[k++] = nums[j++];
    		}
    		else {
    			tmp[k++] = nums[i++];
    		}
    	}
    	while (i <= mid) {
    		tmp[k++] = nums[i++];
    	}
    	while (j <= end) {
    		tmp[k++] = nums[j++];
    	}
    
    	for (int i = sta; i <= end; i++) {
    		nums[i] = tmp[i];
    	}
    }
    
    void mergeSort(int sta, int end) {
    	if (sta < end) {
    		int mid = sta + (end - sta) / 2;
    		mergeSort(sta, mid);
    		mergeSort(mid + 1, end);
    		merge(sta, mid, end);
    	}
    }
    
    int main() {
    	int n, k;
    	cin >> n >> k;
    	for (int i = 0; i < n; i++) {
    		cin >> nums[i];
    	}
    
    	mergeSort(0, n - 1);
    	cout << max((long long)0, reverseCount - k);
    	return 0;
    }
    

40. Entropy

  • 描述

    An entropy encoder is a data encoding method that achieves lossless data compression by encoding a message with “wasted” or “extra” information removed. In other words, entropy encoding removes information that was not necessary in the first place to accurately encode the message. A high degree of entropy implies a message with a great deal of wasted information; english text encoded in ASCII is an example of a message type that has very high entropy. Already compressed messages, such as JPEG graphics or ZIP archives, have very little entropy and do not benefit from further attempts at entropy encoding.

    English text encoded in ASCII has a high degree of entropy because all characters are encoded using the same number of bits, eight. It is a known fact that the letters E, L, N, R, S and T occur at a considerably higher frequency than do most other letters in english text. If a way could be found to encode just these letters with four bits, then the new encoding would be smaller, would contain all the original information, and would have less entropy. ASCII uses a fixed number of bits for a reason, however: it’s easy, since one is always dealing with a fixed number of bits to represent each possible glyph or character. How would an encoding scheme that used four bits for the above letters be able to distinguish between the four-bit codes and eight-bit codes? This seemingly difficult problem is solved using what is known as a “prefix-free variable-length” encoding.

    In such an encoding, any number of bits can be used to represent any glyph, and glyphs not present in the message are simply not encoded. However, in order to be able to recover the information, no bit pattern that encodes a glyph is allowed to be the prefix of any other encoding bit pattern. This allows the encoded bitstream to be read bit by bit, and whenever a set of bits is encountered that represents a glyph, that glyph can be decoded. If the prefix-free constraint was not enforced, then such a decoding would be impossible.

    Consider the text “AAAAABCD”. Using ASCII, encoding this would require 64 bits. If, instead, we encode “A” with the bit pattern “00”, “B” with “01”, “C” with “10”, and “D” with “11” then we can encode this text in only 16 bits; the resulting bit pattern would be “0000000000011011”. This is still a fixed-length encoding, however; we’re using two bits per glyph instead of eight. Since the glyph “A” occurs with greater frequency, could we do better by encoding it with fewer bits? In fact we can, but in order to maintain a prefix-free encoding, some of the other bit patterns will become longer than two bits. An optimal encoding is to encode “A” with “0”, “B” with “10”, “C” with “110”, and “D” with “111”. (This is clearly not the only optimal encoding, as it is obvious that the encodings for B, C and D could be interchanged freely for any given encoding without increasing the size of the final encoded message.) Using this encoding, the message encodes in only 13 bits to “0000010110111”, a compression ratio of 4.9 to 1 (that is, each bit in the final encoded message represents as much information as did 4.9 bits in the original encoding). Read through this bit pattern from left to right and you’ll see that the prefix-free encoding makes it simple to decode this into the original text even though the codes have varying bit lengths.

    As a second example, consider the text “THE CAT IN THE HAT”. In this text, the letter “T” and the space character both occur with the highest frequency, so they will clearly have the shortest encoding bit patterns in an optimal encoding. The letters “C”, "I’ and “N” only occur once, however, so they will have the longest codes.

    There are many possible sets of prefix-free variable-length bit patterns that would yield the optimal encoding, that is, that would allow the text to be encoded in the fewest number of bits. One such optimal encoding is to encode spaces with “00”, “A” with “100”, “C” with “1110”, “E” with “1111”, “H” with “110”, “I” with “1010”, “N” with “1011” and “T” with “01”. The optimal encoding therefore requires only 51 bits compared to the 144 that would be necessary to encode the message with 8-bit ASCII encoding, a compression ratio of 2.8 to 1.

  • 输入描述

    The input file will contain a list of text strings, one per line. The text strings will consist only of uppercase alphanumeric characters and underscores (which are used in place of spaces). The end of the input will be signalled by a line containing only the word “END” as the text string. This line should not be processed.

  • 输出描述

    For each text string in the input, output the length in bits of the 8-bit ASCII encoding, the length in bits of an optimal prefix-free variable-length encoding, and the compression ratio accurate to one decimal point.

  • 参考代码

    
    

41. 小明的购物袋1

  • 描述

    小明去超市购物,他有一只容量为 V 的购物袋,同时他买了 n 件物品,已知每件物品的体积 vi。小明想知道,挑选哪些物品放入购物袋中,可以使袋子剩余的空间最小。

  • 输入描述

    第一行输入一个整数 V(1≤V≤20,000),表示购物袋的容量。
    第二行输入一个整数 n(1≤n≤30),表示蒜头君购买的 n 件物品。

    接下来输入 nn 行,每行输入一个整数 vi(1≤vi≤10,000),表示第 i 件物品的体积。

  • 输出描述

    输出一行,输出一个整数,表示购物袋最小的剩余空间。

  • 参考代码

    #include <iostream>
    #include <vector>
    using namespace std;
    
    int main() {
        int V, n;
        cin >> V >> n;
        vector<int> val(n);
        vector<int> result(V + 1, 0);
    
        for (int i = 0; i < n; i++) {
            cin >> val[i];
        }
        for (int i = 0; i < n; i++) {
            for (int j = V; j >= val[i]; j--) {
                result[j] = max(result[j], result[j - val[i]] + val[i]);
            }
        }
    
        cout << V - result[V];
        return 0;
    }
    

42. 小明的购物袋2

  • 描述

    小明去超市购物,他有一只容量为 V 的购物袋,同时他想买 n 件物品,已知每件物品的体积 vi和重要度 pi。小明想知道,挑选哪些物品放入购物袋中,可以使得买到的物品重要度之和最大,且物品体积和不超过购物袋的容量。

  • 输入描述

    第一行输入两个整数 V(1≤V≤1000)和 n(1≤n≤100)。代表购物袋的总体积为 V,蒜头君一共想买 n 件物品。

    接下来输入 nn 行,每行输入两个整数 vi和 pi(1≤vi,pi≤100),分别表示每件物品的体积和重要度。

  • 输出描述

    输出一行,输出一个整数,表示小明能买到物品的最大重要度之和。

  • 参考代码

    #include <iostream>
    #include <vector>
    using namespace std;
    
    int main() {
        int V, n;
        cin >> V >> n;
        vector<int> val(n);
        vector<int> vol(n);
        vector<int> result(V + 1, 0);
        
        for (int i = 0; i < n; i++) {
            cin >> vol[i] >> val[i];
        }
        for (int i = 0; i < n; i++) {
            for (int j = V; j >= vol[i]; j--) {
                result[j] = max(result[j], result[j - vol[i]] + val[i]);
            }
        }
        
        cout << result[V];
        return 0;
    }
    

43. 小明跳木桩

  • 描述

    小明面前有一排 n 个木桩,木桩的高度分别是h1,h2,h3… hn 。小明第一步可以跳到任意一个木桩,接下来的每一步小明不能往回跳只能往前跳,并且跳下一个木桩的高度 不大于 当前木桩。小明希望能踩到尽量多的木桩,请你帮小明计算,最多能踩到多少个木桩。

  • 输入描述

    第一行输入一个整数 n 代表木桩个数。第二行输入 n 个整数 h1,h2,h3… hn,分别代表 n 个木桩的高度。(1≤n≤1000,1≤hi≤100000)

  • 输出描述

    输出一个整数,代表最多能踩到的木桩个数,占一行。

  • 参考代码

    #include<iostream>
    #include<vector>
    using namespace std;
    
    int main() {
    	int n;
    	int ans = 1;
    	cin >> n;
    	vector<int> height(n);
    	vector<int> dp(n, 1);
    	for (int i = 0; i < n; i++) {
    		cin >> height[i];
    	}
    
    	for (int i = 0; i < n; i++) {
    		for (int j = 0; j < i; j++) {
    			if (height[i] <= height[j]) {
    				dp[i] = max(dp[i], dp[j] + 1);
    			}
    		}
    		ans = max(ans, dp[i]);
    	}
    	cout << ans;
    	return 0;
    }
    

44. 删除最少的元素

  • 描述

    在这里插入图片描述

  • 输入描述

    第一行输入一个整数 n,代表 A 序列中数字的个数。接下来输入 n 个整数,代表A1,A2 ,A3 …An。(1≤n≤1000,1≤Ai≤10000)

  • 输出描述

    输出需要删除的元素个数,占一行。

  • 参考代码

    #include<iostream>
    #include<vector>
    using namespace std;
    
    int main() {
    	int n;
    	cin >> n;
    	vector<int> dp1(n, 1);
    	vector<int> dp2(n, 1);
    	vector<int> num1(n);
    	vector<int> num2(n);
    	int ans = 0;
    
    	for (int i = 0; i < n; i++) {
    		cin >> num1[i];
    		num2[n - i - 1] = num1[i];
    	}
    	for (int i = 0; i < n; i++) {
    		for (int j = 0; j < i; j++) {
    			if (num1[j] >= num1[i]) {
    				dp1[i] = max(dp1[i], dp1[j] + 1);
    			}
    			if (num2[j] >= num2[i]) {
    				dp2[i] = max(dp2[i], dp2[j] + 1);
    			}
    		}
    	}
    	for (int i = 0; i < n; i++) {
    		ans = max(ans, dp1[i] + dp2[i] - 1);
    	}
    
    	cout << n - ans;
    	return 0;
    }
    

45. 最长公共子序列

  • 描述

    这次我们的问题非常简单,小明有两个字符串 a 和 b,小明想知道两个字符串的最长公共子序列的长度。

  • 输入描述

    第一行输入一个字符串。第二行输入一个字符串。(字符串只包含字母,每个字符串长度不超过 1000)

  • 输出描述

    输出二者的最长公共子序列的长度,占一行。

  • 参考代码

    #include<iostream>
    using namespace std;
    
    int main() {
    	string str1;
    	string str2;
    	int dp[1000][1000] = { 0 };
    	cin >> str1 >> str2;
    
    	for (int i = 1; i <= str1.size(); i++) {
    		for (int j = 1; j <= str2.size(); j++) {
    			if (str1[i - 1] == str2[j - 1]) {
    				dp[i][j] = dp[i - 1][j - 1] + 1;
    			}
    			else {
    				dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
    			}
    		}
    	}
    	cout << dp[str1.size()][str2.size()];
    	return 0;
    }
    

46. 回文串

  • 描述

    一个字符串如果从左往右读和从右往左读都一样,那么这个字符串是一个回文串。例 如:”abcba”,”abccba”。
    小明想通过添加字符把一个非回文字符串变成回文串。例如:”trit”,可以添加一 个’i’ 变成回文串”tirit”。请你用程序计算出,对于一个给定的字符串,最少需要 添加几个字符,才能变成回文串。

  • 输入描述

    输入一个长度为n(1≤n≤3000) 的字符串。(字符串只包含字母)

  • 输出描述

    输出最少需要添加的字符个数,占一行。

  • 参考代码

    #include<iostream>
    #include<vector>
    using namespace std;
    
    int main() {
    	string str;
    	cin >> str;
    	int n = str.size();
    	vector<vector<int>> dp(n, vector<int>(n, 0));
    
    	for (int len = 2; len <= n; len++) {
    		for (int i = 0; i <= n - len; i++) {
    			int j = i + len - 1;
    			if (str[i] == str[j]) {
    				dp[i][j] = dp[i + 1][j - 1];
    			}
    			else {
    				dp[i][j] = min(dp[i + 1][j], dp[i][j - 1]) + 1;
    			}
    		}
    	}
    	cout << dp[0][n - 1];
    	return 0;
    }
    
    

47. 灌溉机器人

  • 描述

    农田灌溉是一项十分费体力的农活,特别是大型的农田。小明想为农民伯伯们减轻农作负担,最近在研究一款高科技——灌溉机器人。它可以在远程电脑控制下,给农田里的作物进行灌溉。

    现在有一片 N 行 M 列的农田。农田的土壤有两种类型:类型 H 和类型 P,每一个格子上的土壤类型相同。其中类型 P 的土壤硬度较大,可以用来布置灌溉机器人,但是一个格子上只能布置一台。类型 H 的土壤不能布置灌溉机器人。一台灌溉机器人的灌溉区域如下图所示:

    在这里插入图片描述

    黄色表示灌溉机器人布置的格子,红色表示其灌溉区域,即四个方向上各外扩展两个格子。

    小明想在农田上尽可能多布置一些灌溉机器人,但是任意一台机器人不能在任意一台机器人的灌溉区域里,否则机器容易进水出故障。现在已知农田每个格子的土壤类型,请你来帮小明计算一下,小明最多能布置多少台灌溉机器人

  • 输入描述

    输入第一行输入两个正整数N,M(N≤100,M≤10),表示农田的行和列。

    接下来输入 N 行,每行输入连续的 M 个字符(P或者H),中间没有空格。表示农田每个格子上的土壤类型。

  • 输出描述

    输出一行,输出一个整数,表示最多能摆放的灌溉机器人的数量。

  • 参考代码

    
    

48. 小明的积木

  • 描述

    小明酷爱搭积木,他用积木搭了 n 辆重量为 wi 的小车和一艘最大载重量为 W 的小船,他想用这艘小船将 n 辆小车运输过河。每次小船运载的小车重量不能超过 W。另外,小船在运载小车时, 每辆小车会对小船有一个损坏值 si,当多辆小车一起运载时,该趟运载对小船的损坏值为船上所有小车的最大损坏值。
    现在小明想知道,如何用小船运载 n 辆小车,可以使得对小船造成的总损坏值最小。

  • 输入描述

    第一行输入两个数 W 和 n (100 ≤ W ≤ 400,1 ≤ n ≤ 16),分别表示小船的最大载重量和小车总数。接下来输入 n 行,每行输入两个整数 si 和 wi (1 ≤ si ≤ 50,10 ≤ wi ≤100),分别表示每辆小车对小船的损坏值和每辆小车的重量。

  • 输出描述

    输出一行,输出一个整数,表示用小船运载 n 辆小车,最小的总损坏值。

  • 参考代码

    #include<iostream>
    #include<vector>
    using namespace std;
    
    struct Car {
    	int damage;
    	int weight;
    };
    
    int main() {
    	int W, n;
    	cin >> W >> n;
    	vector<int> dp(1 << n, 0);
    	vector<Car> cars(n);
    
    	for (int i = 0; i < n; i++) {
    		cin >> cars[i].damage >> cars[i].weight;
    	}
    	for (int i = 1; i < (1 << n); i++) {
    		int totalWeight = 0;
    		int maxDamage = 0;
    		
    		for (int j = 0; j < n; j++) {
    			if ((i >> j) & 1) {
    				totalWeight += cars[j].weight;
    				maxDamage = max(maxDamage, cars[i].damage);
    			}
    		}
    		if (totalWeight <= W) {
    			dp[i] = maxDamage;
    		}
    		else {
    			dp[i] = 1000;
    			for (int j = i; j; j = (j - 1) & i) {
    				dp[i] = min(dp[i], dp[j] + dp[j ^ i]);
    			}
    		}
    	}
    
    	cout << dp[(1 << n) - 1];
    	return 0;
    }
    

49. 消除字符串

  • 描述

    小明喜欢中心对称的字符串,即回文字符串。现在小明手里有一个字符串 S, 小明每次都会进行这样的操作:从 S 中挑选一个回文的子序列,将其从字符串 S 中去除,剩下的字符重组成新的字符串 S。 小明想知道,最少可以进行多少次操作,可以消除整个字符串。

  • 输入描述

    输入一行。输入一个字符串 S (1≤length(S)≤16),字符串均由小写字母组成。

  • 输出描述

    输出一行,输出一个整数,表示消除整个字符串需要的最少操作次数。

  • 参考代码

    #include<iostream>
    #include<vector>
    using namespace std;
    
    string str;
    
    bool isReverse(int i) {
    	string str1;
    	string str2;
    
    	for (int j = 0; j < 16; j++) {
    		if ((i >> j) & 1) {
    			str1 += str[j];
    		}
    	}
    	str2 = str1;
    	reverse(str1.begin(), str1.end());
    
    	return str1 == str2;
    }
    
    int main() {
    	cin >> str;
    	int length = str.size();
    	vector<int> dp(1 << length, 0);
    	
    	for (int i = 1; i < (1 << length); i++) {
    		dp[i] = isReverse(i) ? 1 : 16;
    		for (int j = i; j; j = (j - 1) & i) {
    			dp[i] = min(dp[i], dp[j] + dp[j ^ i]);
    		}
    	}
    	cout << dp[(1 << length) - 1];
    	return 0;
    }
    

50. 二叉树中的最低公共祖先

  • 描述

    树中两个结点 U 和 V 的最低公共祖先 ( LCA ) 是指同时具有 U 和 V 作为后代的最深结点。
    给定二叉树中的任何两个结点,请你找到它们的 LCA 。

  • 输入描述

    第一行包含两个整数 M 和 N ,分别表示询问结点对数以及二叉树中的结点数量。
    接下来两行,每行包含 N 个不同的整数,分别表示二叉树的中序和前序遍历。
    保证二叉树可由给定遍历序列唯一确定。
    接下来 M 行,每行包含两个整数 U 和 V ,表示一组询问。

    所有结点权值均在 int 范围内。
    1 ≤ M ≤ 1000
    1 ≤ N ≤ 10000

  • 输出描述

    对于每对给定的 U 和 V ,输出一行结果。

    如果 U 和 V 的 LCA 是 A ,且 A 不是 U 或 V ,则输出 “LCA of U and V is A.”
    如果 U 和 V 的 LCA 是 A ,且 A 是 U 或 V 中的一个,则输出 “X is an ancestor of Y.” ,其中 X 表示 A , Y 表示另一个结点。
    如果 U 或 V 没有在二叉树中找到,则输出 “ERROR: U is not found.” 或 “ERROR: V is not found.” 或 “ERROR: U and V are not found.”

  • 参考代码

    #include<iostream>
    #include<vector>
    #include<map>
    using namespace std;
    
    struct Node {
    	int val;
    	Node* lchild;
    	Node* rchild;
    	Node(int val) : val(val), lchild(NULL), rchild(NULL) {}
    };
    
    vector<int> inorder;
    vector<int> preorder;
    map<int, Node*> record;
    
    Node* createTree(vector<int> preorder, vector<int> inorder, int preL, int preR, int inL, int inR) {
    	if (preL > preR) {
    		return NULL;
    	}
    
    	Node* root = new Node(preorder[preL]);
    	record[preorder[preL]] = root;
    	int k = inL;
    	while (inorder[k] != preorder[preL]) {
    		k++;
    	}
    	int numLeft = k - inL;
    
    	root->lchild = createTree(preorder, inorder, preL + 1, preL + numLeft, inL, k - 1);
    	root->rchild = createTree(preorder, inorder, preL + numLeft + 1, preR, k + 1, inR);
    	return root;
    }
    
    Node* LCA(Node* root, Node* first, Node* second) {
    	if (root == NULL || root == first || root == second) {
    		return root;
    	}
    
    	Node* left = LCA(root->lchild, first, second);
    	Node* right = LCA(root->rchild, first, second);
    	if (left == NULL && right == NULL) {
    		return root;
    	}
    	else if (left == NULL) {
    		return right;
    	}
    	else {
    		return left;
    	}
    }
    
    int main() {
        int M, N;
        cin >> M >> N;
        for (int i = 0; i < N; i++) {
            int order;
            cin >> order;
            inorder.push_back(order);
        }
        for (int i = 0; i < N; i++) {
            int order;
            cin >> order;
            preorder.push_back(order);
        }
        
        Node* root = createTree(preorder, inorder, 0, N - 1, 0, N - 1);
        while (M--) {
            int u, v;
            cin >> u >> v;
            if (record.count(u) && record.count(v)) {
                Node* lca = LCA(root, record[u], record[v]);
                if (lca->val == u) {
                    cout << u << " is an ancestor of " << v << "." << endl;
                }
                else if (lca->val == v) {
                    cout << v << " is an ancestor of " << u << "." << endl;
                }
                else {
                    cout << "LCA of " << u << " and " << v << " is " << lca->val << "." << endl;
                }
            }
            else if (record.count(u)) {
                cout << "ERROR: " << v << " is not found." << endl;
            }
            else if (record.count(v)) {
                cout << "ERROR: " << u << " is not found." << endl;
            }
            else {
                cout << "ERROR: " << u << " and " << v << " are not found." << endl;
            }
        }
        return 0;
    }
    

51. FBI 树

  • 描述

    我们可以把由 “0” 和 “1” 组成的字符串分为三类:全 “0” 串称为 B 串,全 “1” 串称为 I 串,既含 “0” 又含 “1” 的串则称为 F 串。

    FBI 树是一种二叉树,它的结点类型也包括 F 结点,B 结点和 I 结点三种。由一个长度为 2N 的 “01” 串 S 可以构造出一棵 FBI 树 T ,递归的构造方法如下:

    T 的根结点为 R ,其类型与串 S 的类型相同;
    若串 S 的长度大于 1,将串 S 从中间分开,分为等长的左右子串 S1 和 S2 ;由左子串 S1 构造 R 的左子树 T1 ,由右子串 S2 构造 R 的右子树 T2 。
    现在给定一个长度为 2N 的 “01” 串,请用上述构造方法构造出一棵 FBI 树,并输出它的后序遍历序列。

  • 输入描述

    第一行是一个整数 N ( 0 ≤ N ≤ 10 ),
    第二行是一个长度为 2N 的 “01” 串。

  • 参考代码

    #include<iostream>
    using namespace std;
    
    struct Node {
    	char val;
    	Node* lchild;
    	Node* rchild;
    	Node(char val) : val(val), lchild(NULL), rchild(NULL) {}
    };
    
    char classify(string str) {
    	bool contain1 = false;
    	bool contain0 = false;
    
    	for (int i = 0; i < str.size(); i++) {
    		if (str[i] == '0') {
    			contain0 = true;
    		}
    		else {
    			contain1 = true;
    		}
    	}
    	if (contain0 && contain1) {
    		return 'F';
    	}
    	else if (contain0){
    		return 'B';
    	}
    	else {
    		return 'I';
    	}
    }
    
    Node* creatTree(string str) {
    	char val = classify(str);
    	int len = str.size();
    
    	if (len == 0) {
    		return NULL;
    	}
    
    	Node* node = new Node(val);
    	if (len >= 2) {
    		node->lchild = creatTree(str.substr(0, len / 2));
    		node->rchild = creatTree(str.substr(len / 2, len / 2));
    	}
    	return node;
    }
    
    void postorder(Node* node) {
    	if (node) {
    		postorder(node->lchild);
    		postorder(node->rchild);
    		cout << node->val;
    	}
    }
    
    int main() {
    	int N;
    	string str;
    	cin >> N >> str;
    
    	Node* root = creatTree(str);
    	postorder(root);
    
    	return 0;
    }
    

52. 食物链

  • 描述

    动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。
    现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。
    有人用两种说法对这N个动物所构成的食物链关系进行描述:
    第一种说法是"1 X Y",表示X和Y是同类。
    第二种说法是"2 X Y",表示X吃Y。
    此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
    1) 当前的话与前面的某些真的话冲突,就是假话;
    2) 当前的话中X或Y比N大,就是假话;
    3) 当前的话表示X吃X,就是假话。
    你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。

  • 输入描述

    第一行是两个整数N和K,以一个空格分隔。
    以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
    若D=1,则表示X和Y是同类。
    若D=2,则表示X吃Y。

  • 输出描述

    只有一个整数,表示假话的数目。

  • 参考代码

    #include<iostream>
    using namespace std;
    
    class UnionFind {
    private:
    	int father[50000];
    	int dist[50000];
    	int faker;
    	int sum;
    
    public:
    	UnionFind(int n) {
    		for (int i = 1; i <= n; i++) {
    			father[i] = i;
    			dist[i] = 0;
    		}
    		faker = 0;
    		sum = n;
    	}
    
    	int find(int x) {
    		if (father[x] == x) {
    			return x;
    		}
    		int root = find(father[x]);
    		dist[x] += dist[father[x]];
    		return father[x] = root;
    	}
    
    	void merge(int a, int b, int rel) {
    		rel -= 1;
    		int x = find(a);
    		int y = find(b);
    
    		if (a > sum || b > sum) {
    			faker++;
    			return;
    		}
    		if (rel == 1 || a == b) {
    			faker++;
    			return;
    		}
    
    		if (x == y) {
    			if ((((dist[a] - dist[b]) % 3) + 3) % 3 != rel) {
    				faker++;
    				return;
    			}
    		}
    		else {
    			father[x] = y;
    			dist[x] = dist[b] - (dist[a] - rel);
    		}
    	}
    
    	int getFaker() {
    		return this->faker;
    	}
    };
    
    int main() {
    	int N, K;
    	cin >> N >> K;
    	UnionFind foodChain(N);
    
    	for (int i = 0; i < K; i++) {
    		int rel, a, b;
    		cin >> rel >> a >> b;
    		foodChain.merge(a, b, rel);
    	}
    	cout << foodChain.getFaker();
    	return 0;
    }
    

53. 求二叉树高度

  • 描述

    说明二叉树的两指针域为lchild 与rchild,算法中p为二叉树的根,lh和rh分别为以p为根的二叉树的左子树和右子树的高,hi为以p为根的二叉树的高,hi最后返回。

  • 输入描述

    输入为一颗二叉树,模板会处理相关输入

  • 输出描述

    模板会输出二叉树的高度

  • 参考代码

    #include<iostream>
    #include<queue>
    #include<sstream>
    using namespace std;
    
    struct Node {
        int val;
        Node* lchild;
        Node* rchild;
        Node(int val) : val(val), lchild(NULL), rchild(NULL) {}
    };
    
    void trimLeftTailingSpace(string& input) {
        input.erase(input.begin(), find_if(input.begin(), input.end(), [](int ch) {
            return !isspace(ch);
            }));
    }
    
    void trimRightTailingSpace(string& input) {
        input.erase(find_if(input.rbegin(), input.rend(), [](int ch) {
            return !isspace(ch);
            }).base(), input.end());
    }
    
    Node* createTree(string input) {
        trimLeftTailingSpace(input);
        trimRightTailingSpace(input);
        input = input.substr(1, input.size() - 2);
    
        string val;
        stringstream ss;
        ss.str(input);
    
        getline(ss, val, ',');
        queue<Node*> tree;
        Node* root = new Node(stoi(val));
        tree.push(root);
    
        while (!tree.empty()) {
            Node* cur = tree.front();
            tree.pop();
    
            if (!getline(ss, val, ',')) {
                break;
            }
            trimLeftTailingSpace(val);
            if (val != "null") {
                cur->lchild = new Node(stoi(val));
                tree.push(cur->lchild);
            }
    
            if (!getline(ss, val, ',')) {
                break;
            }
            trimLeftTailingSpace(val);
            if (val != "null") {
                cur->rchild = new Node(stoi(val));
                tree.push(cur->rchild);
            }
        }
    
        return root;
    }
    
    int height(Node* root) {
        if (root == nullptr) {
            return 0;
        }
    
        int lh = height(root->lchild);
        int rh = height(root->rchild);
    
        return max(lh, rh) + 1;
    }
    
    int main() {
        string line;
        getline(cin, line);
        Node* root = createTree(line);
    
        cout << height(root) << endl;
        return 0;
    }
    

54. 新二叉树

  • 描述

    输入一串二叉树,输出其前序遍历。

  • 输入描述

    第一行为二叉树的节点数 n(1≤n≤26)。
    后面 n 行,每一个字母为节点,后两个字母分别为其左右儿子。
    空节点用 * 表示。

  • 输出描述

    输出二叉树的前序遍历。

  • 参考代码

    #include<iostream>
    #include<map>
    using namespace std;
    
    map<char, pair<char, char>> tree;
    
    void preorder(char root) {
    	if (root != '*') {
    		cout << root;
    		preorder(tree[root].first);
    		preorder(tree[root].second);
    	}
    }
    
    int main() {
    	int n;
    	char root;
    
    	cin >> n;
    	for (int i = 0; i < n; i++) {
    		string node;
    		cin >> node;
    		tree[node[0]] = { node[1], node[2] };
    		if (i == 0) {
    			root = node[0];
    		}
    	}
    	preorder(root);
    	
    	return 0;
    }
    

55. 先序遍历二叉树

  • 描述

    下面是先序遍历二叉树的非递归子程序,请阅读子程序,充空格使其成为完整的算法

  • 输入描述

    模板已自动处理

  • 输出描述

    输出前序遍历二叉树的结果

  • 参考代码

    #include<iostream>
    #include<queue>
    #include<sstream>
    using namespace std;
    
    struct Node {
        int val;
        Node* lchild;
        Node* rchild;
        Node(int val) : val(val), lchild(NULL), rchild(NULL) {}
    };
    
    void trimLeftTailingSpace(string& input) {
        input.erase(input.begin(), find_if(input.begin(), input.end(), [](int ch) {
            return !isspace(ch);
            }));
    }
    
    void trimRightTailingSpace(string& input) {
        input.erase(find_if(input.rbegin(), input.rend(), [](int ch) {
            return !isspace(ch);
            }).base(), input.end());
    }
    
    Node* createTree(string input) {
        trimLeftTailingSpace(input);
        trimRightTailingSpace(input);
        input = input.substr(1, input.size() - 2);
    
        string val;
        stringstream ss;
        ss.str(input);
    
        getline(ss, val, ',');
        queue<Node*> tree;
        Node* root = new Node(stoi(val));
        tree.push(root);
    
        while (!tree.empty()) {
            Node* cur = tree.front();
            tree.pop();
    
            if (!getline(ss, val, ',')) {
                break;
            }
            trimLeftTailingSpace(val);
            if (val != "null") {
                cur->lchild = new Node(stoi(val));
                tree.push(cur->lchild);
            }
    
            if (!getline(ss, val, ',')) {
                break;
            }
            trimLeftTailingSpace(val);
            if (val != "null") {
                cur->rchild = new Node(stoi(val));
                tree.push(cur->rchild);
            }
        }
    
        return root;
    }
    
    void preorder(Node* root) {
        if (root) {
            cout << root->val << endl;
            preorder(root->lchild);
            preorder(root->rchild);
        }
    }
    
    int main() {
        string line;
        getline(cin, line);
        Node* root = createTree(line);
    
        preorder(root);
        return 0;
    }
    

56. 朋友

  • 描述

    在社交的过程中,通过朋友,也能认识新的朋友。在某个朋友关系图中,假定 A 和 B 是朋友,B 和 C 是朋友,那么 A 和 C 也会成为朋友。即,我们规定朋友的朋友也是朋友。
    现在,已知若干对朋友关系,询问某两个人是不是朋友。
    请编写一个程序来解决这个问题吧。

  • 输入描述

    第一行:三个整数n,m,p(n≤5000,m≤5000,p≤5000),分别表示有 n 个人,m 个朋友关系,询问 p 对朋友关系。

    接下来 m 行:每行两个数 Ai,Bi,1≤Ai,Bi≤N,表示 Ai和 Bi具有朋友关系。

    接下来 p 行:每行两个数,询问两人是否为朋友。

  • 输出描述

    输出共 p 行,每行一个Yes或No。表示第 i 个询问的答案为是否朋友。

  • 参考代码

    #include<iostream>
    using namespace std;
    
    class UnionFind {
    private:
    	int father[5000];
    	int height[5000];
    
    public:
    	UnionFind(int n) {
    		for (int i = 1; i <= n; i++) {
    			father[i] = i;
    			height[i] = 1;
    		}
    	}
    
    	int get(int x) {
    		if (x == father[x]) {
    			return x;
    		}
    		return father[x] = get(father[x]);
    	}
    
    	void merge(int x, int y) {
    		x = get(x);
    		y = get(y);
    
    		if (x == y) {
    			return;
    		}
    		else {
    			if (height[x] == height[y]) {
    				height[x]++;
    				father[y] = x;
    			}
    			else if (height[x] > height[y]) {
    				father[y] = x;
    			}
    			else {
    				father[x] = y;
    			}
    		}
    	}
    };
    
    int main() {
    	int n, m, p;
    	cin >> n >> m >> p;
    	UnionFind friends(n);
    
    	for (int i = 0; i < m; i++) {
    		int x, y;
    		cin >> x >> y;
    		friends.merge(x, y);
    	}
    	for (int i = 0; i < p; i++) {
    		int x, y;
    		cin >> x >> y;
    		if (friends.get(x) == friends.get(y)) {
    			cout << "Yes" << endl;
    		}
    		else {
    			cout << "No" << endl;
    		}
    	}
    
    	return 0;
    }
    

57. 网络交友

  • 描述

    在网络社交的过程中,通过朋友,也能认识新的朋友。在某个朋友关系图中,假定 A 和 B 是朋友,B 和 C 是朋友,那么 A 和 C 也会成为朋友。即,我们规定朋友的朋友也是朋友。

    现在要求你每当有一对新的朋友认识的时候,你需要计算两人的朋友圈合并以后的大小。

  • 输入描述

    第一行:一个整数n(n≤5000),表示有 n 对朋友认识。

    接下来 n 行:每行输入两个名字。表示新认识的两人的名字,用空格隔开。(名字是一个首字母大写后面全是小写字母且长度不超过 20 的串)。

  • 输出描述

    对于每一对新认识的朋友,输出合并以后的朋友圈的大小。

  • 参考代码

    #include<iostream>
    #include<map>
    using namespace std;
    
    class UnionFind {
    private:
    	map<string, string> father;
    	map<string, int> height;
    	map<string, int> volumn;
    
    public:
    	void add(string name) {
    		if (father.count(name)) {
    			return;
    		}
    		father[name] = name;
    		height[name] = 1;
    		volumn[name] = 1;
    	}
    
    	int getVolumn(string name) {
    		name = get(name);
    		return volumn[name];
    	}
    
    	string get(string x) {
    		if (x == father[x]) {
    			return x;
    		}
    		return father[x] = get(father[x]);
    	}
    
    	void merge(string x, string y) {
    		x = get(x);
    		y = get(y);
    
    		if (x == y) {
    			return;
    		}
    		else {
    			if (height[x] == height[y]) {
    				height[x]++;
    				father[y] = x;
    				volumn[x] += volumn[y];
    			}
    			else if (height[x] > height[y]) {
    				father[y] = x;
    				volumn[x] += volumn[y];
    			}
    			else {
    				father[x] = y;
    				volumn[y] += volumn[x];
    			}
    		}
    	}
    };
    
    int main() {
    	int n;
    	cin >> n;
    	UnionFind friends;
    
    	for (int i = 0; i < n; i++) {
    		string x, y;
    		cin >> x >> y;
    		friends.add(x);
    		friends.add(y);
    		friends.merge(x, y);
    		cout << friends.getVolumn(x) << endl;
    	}
    
    	return 0;
    }
    

58. 找出所有谎言

  • 描述

    蒜头君有很多卡片,每张卡片正面上印着“剪刀”,“石头”或者“布”三种图案中的一种,反面则印着卡片的序号。“剪刀”,“石头”和“布”三种构成了一个有趣的环形,“剪刀”可以战胜“布”,“布”可以战胜“石头”,“石头”可以战胜“剪刀”。

    现有 NN 张卡片,以 1-N 编号。每张卡片印着“剪刀”,“石头”,“布”中的一种,但是我们并不知道它到底是哪一种。

    有人用两种说法对这 N 张卡片所构成的关系进行描述:

    第一种说法是“1 X Y”,表示 X 号卡片和 Y 号卡片是同一种卡片。

    第二种说法是“2 X Y”,表示 X 号卡片可以战胜 Y 号卡片。

    蒜头君对 N 张卡片,用上述两种说法,一句接一句地说出 K 句话,这 K 句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。

    1. 当前的话与前面的某些真的话冲突,就是假话;
    2. 当前的话中 X 或 Y 的值比 N 大,就是假话;
    3. 当前的话表示 X 能战胜 X ,就是假话。

    你的任务是根据给定的 N 和 K 句话,计算假话的总数。

  • 输入描述

    第一行是两个整数N(1≤N≤50,000) 和K(0≤K≤100,000),以一个空格分隔。

    以下 K 行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中 D 表示说法的种类。

    若 D=1,则表示 X 和 Y 是同一种卡片。

    若 D=2,则表示 X 能战胜 Y。

  • 输出描述

    只有一个整数,表示假话的数目。

  • 参考代码

    #include<iostream>
    using namespace std;
    
    class UnionFind {
    private:
    	int father[50000];
    	int dist[50000];
    	int faker;
    	int sum;
    
    public:
    	UnionFind(int n) {
    		for (int i = 1; i <= n; i++) {
    			father[i] = i;
    			dist[i] = 0;
    		}
    		faker = 0;
    		sum = n;
    	}
    
    	int find(int x) {
    		if (father[x] == x) {
    			return x;
    		}
    		int root = find(father[x]);
    		dist[x] += dist[father[x]];
    		return father[x] = root;
    	}
    
    	void merge(int a, int b, int rel) {
    		rel -= 1;
    		int x = find(a);
    		int y = find(b);
    
    		if (a > sum || b > sum) {
    			faker++;
    			return;
    		}
    		if (rel == 1 || a == b) {
    			faker++;
    			return;
    		}
    
    		if (x == y) {
    			if ((((dist[a] - dist[b]) % 3) + 3) % 3 != rel) {
    				faker++;
    				return;
    			}
    		}
    		else {
    			father[x] = y;
    			dist[x] = dist[b] - (dist[a] - rel);
    		}
    	}
    
    	int getFaker() {
    		return this->faker;
    	}
    };
    
    int main() {
    	int N, K;
    	cin >> N >> K;
    	UnionFind foodChain(N);
    
    	for (int i = 0; i < K; i++) {
    		int rel, a, b;
    		cin >> rel >> a >> b;
    		foodChain.merge(a, b, rel);
    	}
    	cout << foodChain.getFaker();
    	return 0;
    }
    

59. 接龙

  • 描述

    蒜头君在玩一种接龙的游戏,蒜头君有30000 张卡片分别放在 30000 列,每列依次编号为 1,2,…,30000。同时,蒜头君也把每张卡片依次编号为 1,2,…,30000。

    游戏开始,蒜头君让让第 i 张卡片处于第 i(i=1,2,…,30000) 列。然后蒜头君会发出多次指令,每次调动指令 M i j 会将第 i 张卡片所在的队列的所有卡片,作为一个整体(头在前尾在后)接至第 jj 张卡片所在的队列的尾部。

    蒜头君还会查看当前的情况,发出 C i j 的指令,即询问电脑,第 i 张卡片与第 j 张卡片当前是否在同一个队列中,如果在同一列中,那么它们之间一共有多少张卡片。

    聪明的你能不能编写程序处理蒜头君的指令,以及回答蒜头君的询问呢?

  • 输入描述

    第一行有一个整数 T(1≤T≤500000),表示总共有 T 条指令。

    以下有 T 行,每行有一条指令。指令有两种格式:

    1. M i j:i 和 j 是两个整数(1≤i,j≤30000),表示指令涉及的卡片编号。你需要让第 i 张卡片所在的队列的所有卡片,作为一个整体(头在前尾在后)接至第 j 张卡片所在的队列的尾部,输入保证第 i 号卡片与第 j 号卡片不在同一列。
    2. C i j:i 和 j 是两个整数(1≤i,j≤30000),表示指令涉及的卡片编号。该指令是蒜头君的询问指令。
  • 输出描述

    如果是蒜头君调动指令,则表示卡片排列发生了变化,你的程序要注意到这一点,但是不要输出任何信息;

    如果是蒜头君的询问指令,你的程序要输出一行,仅包含一个整数,表示在同一列上,第 i 号卡片与第 jj 号卡片之间的卡片数目(不包括第 i 张卡片和第 j 张卡片)。如果第 i 号卡片与第 j 号卡片当前不在同一个队列种中,则输出 −1。

  • 参考代码

    #include<iostream>
    using namespace std;
    
    class UnionFind {
    private:
    	int father[30000];
    	int dist[30000];
    	int size[30000];
    
    public:
    	UnionFind() {
    		for (int i = 1; i < 30000; i++) {
    			father[i] = i;
    			dist[i] = 0;
    			size[i] = 1;
    		}
    	}
    
    	int find(int x) {
    		if (x == father[x]) {
    			return x;
    		}
    
    		int root = find(father[x]);
    		dist[x] += dist[father[x]];
    		return father[x] = root;
    	}
    
    	void merge(int a, int b) {
    		int x = find(a);
    		int y = find(b);
    
    		if (x == y) {
    			return;
    		}
    		else {
    			father[x] = y;
    			dist[x] += size[y];
    			size[y] += size[x];
    		}
    	}
    
    	int check(int a, int b) {
    		int x = find(a);
    		int y = find(b);
    
    		if (x != y) {
    			return -1;
    		}
    		else {
    			return abs(dist[a] - dist[b] - 1);
    		}
    	}
    };
    
    int main() {
    	int n;
    	UnionFind queue;
    	cin >> n;
    
    	for (int i = 0; i < n; i++) {
    		char flag;
    		int a, b;
    		cin >> flag >> a >> b;
    
    		if (flag == 'M') {
    			queue.merge(b, a);
    		}
    		else {
    			cout << queue.check(a, b) << endl;
    		}
    	}
    	return 0;
    }
    

60. 在二叉树上移动

  • 描述

    有一个完全二叉树(满二叉树)上有 2LAYERS-1 (LAYERS = 10100) 个节点,按顺序标号 1,2,…,2LAYERS-1。
    节点1是根节点,对于每一个非叶节点 i ( 1 ≤ i < 2LAYERS-1 ) ,它都有左孩子节点 2i 和右孩子节点 2i + 1。
    小明从节点X开始,执行N步操作。操作用字符序列表示(字符串),字符的意义如下:

    • U:从当前节点跳转其父节点
    • L:从当前节点跳转其左孩子节点
    • R:从当前节点跳转其右孩子节点

    找到 N 步操作以后小明所处的节点编号。题目保证所有用例的答案最大为 1018 ,同时保证对于所有给定的字符串中,小明不会在根节点上执行 U 操作,也不会在叶子节点执行 L 或 R 操作。

  • 输入描述

    输入按如下格式给出:

    N X
    S
    

    1 ≤ N ≤ 106
    1 ≤ X ≤ 1018
    S 为只包含 U、L、R 字符,且长度为 N 的字符串。

  • 输出描述

    输出最后所在的节点编号。

  • 参考代码

    #include<iostream>
    #include<queue>
    using namespace std;
    
    string preProcess(string str) {
    	string result;
    	result.push_back(str[0]);
    
    	for (int i = 1; i < str.size(); i++) {
    		if (str[i] == 'U') {
    			if (result.back() == 'L' || result.back() == 'R') {
    				result.pop_back();
    				continue;
    			}
    		}
    		result.push_back(str[i]);
    	}
    
    	return result;
    }
    
    int main() {
    	long long N, X;
    	string str;
    	cin >> N >> X;
    	cin >> str;
    
    	str = preProcess(str);
    	for (int i = 0; i < str.size(); i++) {
    		if (str[i] == 'U') {
    			X /= 2;
    		}
    		else if (str[i] == 'L') {
    			X *= 2;
    		}
    		else {
    			X = X * 2 + 1;
    		}
    	}
    	cout << X;
    
    	return 0;
    }
    

61. 二叉搜索树

  • 描述

    判断两序列是否为同一二叉搜索树序列。

  • 输入描述

    第一行是一个数 n ( 1 <= n <= 20 ),表示有n个需要判断。
    接下去一行是一个序列,序列长度小于 10 ,包含 0 ~ 9 的数字,没有重复数字,根据这个序列可以构造出一颗二叉搜索树。
    接下去的 n 行有 n 个序列,每个序列格式跟第一个序列一样,请判断这两个序列是否能组成同一颗二叉搜索树。

  • 输出描述

    如果序列相同则输出YES,否则输出NO

  • 参考代码

    #include<iostream>
    using namespace std;
    
    struct TreeNode {
        int val;
        TreeNode* lchild;
        TreeNode* rchild;
        TreeNode(int val) : val(val), lchild(NULL), rchild(NULL) {}
    };
    
    TreeNode* buildBST(string str) {
        int val = str[0] - '0';
        TreeNode* root = new TreeNode(val);
    
        for (int i = 1; i < str.size(); i++) {
            int val = str[i] - '0';
            TreeNode* node = new TreeNode(val);
            TreeNode* cur = root;
            TreeNode* parent = root;
    
            while (cur != NULL) {
                parent = cur;
                if (val < cur->val) {
                    cur = cur->lchild;
                }
                else {
                    cur = cur->rchild;
                }
            }
            if (val < parent->val) {
                parent->lchild = node;
            }
            else {
                parent->rchild = node;
            }
        }
    
        return root;
    }
    
    bool isSameBST(TreeNode* root1, TreeNode* root2) {
        if (root1 == NULL && root2 == NULL)
            return true;
        if (root1 == NULL || root2 == NULL)
            return false;
        return (root1->val == root2->val) && isSameBST(root1->lchild, root2->lchild) && isSameBST(root1->rchild, root2->rchild);
    }
    
    int main() {
        int n;
        string strBase;
        cin >> n;
        cin >> strBase;
        TreeNode* treeBase = buildBST(strBase);
    
        for (int i = 0; i < n; ++i) {
            string str;
            cin >> str;
            TreeNode* tree = buildBST(str);
            if (isSameBST(treeBase, tree)) {
                cout << "YES" << endl;
            }
            else {
                cout << "NO" << endl;
            }
        }
    
        return 0;
    }
    

62. 二叉树遍历

  • 描述

    二叉树是一组有限的顶点,要么是空的,要么由一个根 r 和两个不相交的二叉树组成,称为左子树和右子树。 可以通过三种最重要的方式系统地遍历或排序二叉树的顶点。 它们是预购、中购和后购。 令 T 为具有根 r 和子树 T1、T2 的二叉树。

    在 T 的顶点的前序遍历中,我们先访问根 r,然后按前序访问 T1 的顶点,然后按前序访问 T2 的顶点。

    在对 T 的顶点进行中序遍历时,我们按中序访问 T1 的顶点,然后是根 r,然后按中序访问 T2 的顶点。

    在 T 的顶点的后序遍历中,我们按后序访问 T1 的顶点,然后按后序访问 T2 的顶点,最后访问 r。

    现在给定某个二叉树的前序序列和中序序列。 尝试找出它的后序序列。

  • 输入描述

    输入包含几个测试用例。 每个测试用例的第一行包含一个整数 n (1<=n<=1000),即二叉树的顶点数。 后面是两行,分别表示前序序列和中序序列。 您可以假设它们始终对应于排他二叉树。

  • 输出描述

    对于每个测试用例,打印一行指定相应的后序序列。

  • 参考代码

    #include<iostream>
    #include<vector>
    #include<map>
    using namespace std;
    
    struct Node {
        int val;
        Node* lchild;
        Node* rchild;
        Node(int val) : val(val), lchild(NULL), rchild(NULL) {}
    };
    
    vector<int> inorder;
    vector<int> preorder;
    
    Node* createTree(vector<int> preorder, vector<int> inorder, int preL, int preR, int inL, int inR) {
        if (preL > preR) {
            return NULL;
        }
    
        Node* root = new Node(preorder[preL]);
        int k = inL;
        while (inorder[k] != preorder[preL]) {
            k++;
        }
        int numLeft = k - inL;
    
        root->lchild = createTree(preorder, inorder, preL + 1, preL + numLeft, inL, k - 1);
        root->rchild = createTree(preorder, inorder, preL + numLeft + 1, preR, k + 1, inR);
        return root;
    }
    
    void postorder(Node* root) {
        if (root) {
            postorder(root->lchild);
            postorder(root->rchild);
            cout << root->val << " ";
        }
    }
    
    int main() {
        int N;
        cin >> N;
        for (int i = 0; i < N; i++) {
            int order;
            cin >> order;
            preorder.push_back(order);
        }
        for (int i = 0; i < N; i++) {
            int order;
            cin >> order;
            inorder.push_back(order);
        }
    
        Node* root = createTree(preorder, inorder, 0, N - 1, 0, N - 1);
        postorder(root);
    
        return 0;
    }
    

63. 二叉树

  • 描述

    相信大家都学了数据结构了吧,那么关于二叉树的描述我就不细说了。现在给你二叉树的前序遍历,和中序遍历,让你求出它的后序遍历。

  • 输入描述

    多组测试数据,每组第一行一个整数n(n<100)。接下来输入这个树的前序,和中序遍历。

  • 输出描述

    将其后序遍历,并换行。

  • 参考代码

    #include<iostream>
    #include<vector>
    #include<map>
    using namespace std;
    
    struct Node {
        int val;
        Node* lchild;
        Node* rchild;
        Node(int val) : val(val), lchild(NULL), rchild(NULL) {}
    };
    
    vector<int> post;
    
    Node* createTree(vector<int> preorder, vector<int> inorder, int preL, int preR, int inL, int inR) {
        if (preL > preR) {
            return NULL;
        }
    
        Node* root = new Node(preorder[preL]);
        int k = inL;
        while (inorder[k] != preorder[preL]) {
            k++;
        }
        int numLeft = k - inL;
    
        root->lchild = createTree(preorder, inorder, preL + 1, preL + numLeft, inL, k - 1);
        root->rchild = createTree(preorder, inorder, preL + numLeft + 1, preR, k + 1, inR);
        return root;
    }
    
    void postorder(Node* root) {
        if (root) {
            postorder(root->lchild);
            postorder(root->rchild);
            post.push_back(root->val);
        }
    }
    
    int main() {
        int N;
    
        while (cin >> N) {
            vector<int> inorder;
            vector<int> preorder;
            post.clear();
            for (int i = 0; i < N; i++) {
                int order;
                cin >> order;
                preorder.push_back(order);
            }
            for (int i = 0; i < N; i++) {
                int order;
                cin >> order;
                inorder.push_back(order);
            }
    
            Node* root = createTree(preorder, inorder, 0, N - 1, 0, N - 1);
            postorder(root);
    
            for (int i = 0; i < N; i++) {
                cout << post[i] << " ";
            }
            cout << endl;
        }
    
        return 0;
    }
    

64. 线索二叉树的中序遍历

  • 描述

    下面是中序线索树的遍历算法,树有头结点且由指针thr 指向。树的结点有五个域,分别为:数据域data,左、右孩子域lchild和 rchild,左、右标志域ltag和 rtag。规定标志域为1是线索,0是指向孩子的指针。

  • 输入描述

    模板自动处理输入

  • 输出描述

    输出中序遍历的结果

  • 参考代码

    #include<string>
    #include<iostream>
    #include <queue>
    #include <algorithm>
    #include <sstream>
    using namespace std;
    
    struct TreeNode {
        int val;
        TreeNode* lchild;
        TreeNode* rchild;
        int ltag;
        int rtag;
        TreeNode(int x) : val(x), ltag(0), rtag(0), lchild(NULL), rchild(NULL) {}
    };
    
    
    class Solution {
    public:
        void inorderthread(TreeNode* thr) {
            TreeNode* cur = thr;
            while (cur) {
                while (!thr->ltag) {
                    cur = cur->lchild;
                }
                cout << cur->val << endl;
                while (thr->rtag) {
                    cur = cur->rchild;
                    cout << cur->val << endl;
                }
                cur = cur->rchild;
            }
        }
    };
    
    
    
    void trimLeftTrailingSpaces(string& input) {
        input.erase(input.begin(), find_if(input.begin(), input.end(), [](int ch) {
            return !isspace(ch);
            }));
    }
    
    void trimRightTrailingSpaces(string& input) {
        input.erase(find_if(input.rbegin(), input.rend(), [](int ch) {
            return !isspace(ch);
            }).base(), input.end());
    }
    
    
    void inThread(TreeNode* p, TreeNode*& pre) {
        if (p) {
            inThread(p->lchild, pre); //线索化左子树
            if (p->lchild == NULL) {
                p->lchild = pre;
                p->ltag = 1;
            }
            if (pre != NULL && pre->rchild == NULL) {
                pre->rchild = p;
                pre->rtag = 1;
            }
            pre = p;
            inThread(p->rchild, pre);
        }
    }
    
    void createThreadTree(TreeNode* root) {
        TreeNode* pre = NULL;
        if (root) {
            inThread(root, pre);
        }
    }
    
    
    TreeNode* stringToTreeNode(string input) {
        trimLeftTrailingSpaces(input);
        trimRightTrailingSpaces(input);
        input = input.substr(1, input.length() - 2);
        if (!input.size()) {
            return nullptr;
        }
    
        string item;
        stringstream ss;
        ss.str(input);
    
        getline(ss, item, ',');
        TreeNode* root = new TreeNode(stoi(item));
        queue<TreeNode*> nodeQueue;
        nodeQueue.push(root);
    
        while (true) {
            TreeNode* node = nodeQueue.front();
            nodeQueue.pop();
    
            if (!getline(ss, item, ',')) {
                break;
            }
    
            trimLeftTrailingSpaces(item);
            if (item != "null") {
                int leftNumber = stoi(item);
                node->lchild = new TreeNode(leftNumber);
                nodeQueue.push(node->lchild);
            }
    
            if (!getline(ss, item, ',')) {
                break;
            }
    
            trimLeftTrailingSpaces(item);
            if (item != "null") {
                int rightNumber = stoi(item);
                node->rchild = new TreeNode(rightNumber);
                nodeQueue.push(node->rchild);
            }
        }
        return root;
    }
    
    int main() {
        string line;
        getline(cin, line);
        TreeNode* root = stringToTreeNode(line);
        createThreadTree(root);
        Solution().inorderthread(root);
        //TravIn_Thread_BT(root);
        return 0;
    }
    
    
    

65. Trees on the level

  • 描述

    Trees are fundamental in many branches of computer science. Current state-of-the art parallel computers such as Thinking Machines’ CM-5 are based on fat trees. Quad- and octal-trees are fundamental to many algorithms in computer graphics.

    This problem involves building and traversing binary trees.
    Given a sequence of binary trees, you are to write a program that prints a level-order traversal of each tree. In this problem each node of a binary tree contains a positive integer and all binary trees have have fewer than 256 nodes.

    In a level-order traversal of a tree, the data in all nodes at a given level are printed in left-to-right order and all nodes at level k are printed before all nodes at level k+1.

    For example, a level order traversal of the treeis: 5, 4, 8, 11, 13, 4, 7, 2, 1.

    In this problem a binary tree is specified by a sequence of pairs (n,s) where n is the value at the node whose path from the root is given by the string s. A path is given be a sequence of L’s and R’s where L indicates a left branch and R indicates a right branch. In the tree diagrammed above, the node containing 13 is specified by (13,RL), and the node containing 2 is specified by (2,LLR). The root node is specified by (5,) where the empty string indicates the path from the root to itself. A binary tree is considered to be completely specified if every node on all root-to-node paths in the tree is given a value exactly once.

  • 输入描述

    The input is a sequence of binary trees specified as described above. Each tree in a sequence consists of several pairs (n,s) as described above separated by whitespace. The last entry in each tree is (). No whitespace appears between left and right parentheses.

    All nodes contain a positive integer. Every tree in the input will consist of at least one node and no more than 256 nodes. Input is terminated by end-of-file.

  • 输出描述

    For each completely specified binary tree in the input file, the level order traversal of that tree should be printed. If a tree is not completely specified, i.e., some node in the tree is NOT given a value or a node is given a value more than once, then the string ``not complete’’ should be printed

  • 参考代码

    
    

66. Graph and Queries

  • 描述

    You are given an undirected graph with N vertexes and M edges. Every vertex in this graph has an integer value assigned to it at the beginning. You’re also given a sequence of operations and you need to process them as requested. Here’s a list of the possible operations that you might encounter:

    1. Deletes an edge from the graph.

    The format is [D X], where X is an integer from 1 to M, indicating the ID of the edge that you should delete. It is guaranteed that no edge will be deleted more than once.

    1. Queries the weight of the vertex with K-th maximum value among all vertexes currently connected with vertex X (including X itself).

    The format is [Q X K], where X is an integer from 1 to N, indicating the id of the vertex, and you may assume that K will always fit into a 32-bit signed integer. In case K is illegal, the value for that query will be considered as undefined, and you should return 0 as the answer to that query.

    1. Changes the weight of a vertex.

    The format is [C X V], where X is an integer from 1 to N, and V is an integer within the range [-106, 106].

    The operations end with one single character, E, which indicates that the current case has ended.
    For simplicity, you only need to output one real number - the average answer of all queries.

  • 输入描述

    There are multiple test cases in the input file. Each case starts with two integers N and M (1 <= N <= 2 * 104, 0 <= M <= 6 * 104), the number of vertexes in the graph. The next N lines describes the initial weight of each vertex (-106 <= weight[i] <= 106). The next part of each test case describes the edges in the graph at the beginning. Vertexes are numbered from 1 to N. The last part of each test case describes the operations to be performed on the graph. It is guaranteed that the number of query operations [Q X K] in each case will be in the range [1, 2 * 105], and there will be no more than 2 * 105 operations that change the values of the vertexes [C X V].

    There will be a blank line between two successive cases. A case with N = 0, M = 0 indicates the end of the input file and this case should not be processed by your program.

  • 输出描述

    For each test case, output one real number �C the average answer of all queries, in the format as indicated in the sample output. Please note that the result is rounded to six decimal places.

  • 参考代码

    
    

67. Shaolin

  • 描述

    Shaolin temple is very famous for its Kongfu monks.A lot of young men go to Shaolin temple every year, trying to be a monk there. The master of Shaolin evaluates a young man mainly by his talent on understanding the Buddism scripture, but fighting skill is also taken into account.
    When a young man passes all the tests and is declared a new monk of Shaolin, there will be a fight , as a part of the welcome party. Every monk has an unique id and a unique fighting grade, which are all integers. The new monk must fight with a old monk whose fighting grade is closest to his fighting grade. If there are two old monks satisfying that condition, the new monk will take the one whose fighting grade is less than his.
    The master is the first monk in Shaolin, his id is 1,and his fighting grade is 1,000,000,000.He just lost the fighting records. But he still remembers who joined Shaolin earlier, who joined later. Please recover the fighting records for him.

  • 输入描述

    There are several test cases.
    In each test case:
    The first line is a integer n (0 <n <=100,000),meaning the number of monks who joined Shaolin after the master did.(The master is not included).Then n lines follow. Each line has two integer k and g, meaning a monk’s id and his fighting grade.( 0<= k ,g<=5,000,000)
    The monks are listed by ascending order of jointing time.In other words, monks who joined Shaolin earlier come first.
    The input ends with n = 0.

  • 输出描述

    A fight can be described as two ids of the monks who make that fight. For each test case, output all fights by the ascending order of happening time. Each fight in a line. For each fight, print the new monk’s id first ,then the old monk’s id.

  • 参考代码

    
    

68. Robotic Sort

  • 描述

    Somewhere deep in the Czech Technical University buildings, there are laboratories for examining mechanical and electrical properties of various materials. In one of yesterday’s presentations, you have seen how was one of the laboratories changed into a new multimedia lab. But there are still others, serving to their original purposes.

    In this task, you are to write software for a robot that handles samples in such a laboratory. Imagine there are material samples lined up on a running belt. The samples have different heights, which may cause troubles to the next processing unit. To eliminate such troubles, we need to sort the samples by their height into the ascending order.

    Reordering is done by a mechanical robot arm, which is able to pick up any number of consecutive samples and turn them round, such that their mutual order is reversed. In other words, one robot operation can reverse the order of samples on positions between A and B.

    A possible way to sort the samples is to find the position of the smallest one (P1) and reverse the order between positions 1 and P1, which causes the smallest sample to become first. Then we find the second one on position P and reverse the order between 2 and P2. Then the third sample is located etc.

    The picture shows a simple example of 6 samples. The smallest one is on the 4th position, therefore, the robot arm reverses the first 4 samples. The second smallest sample is the last one, so the next robot operation will reverse the order of five samples on positions 2-6. The third step will be to reverse the samples 3-4, etc.

    Your task is to find the correct sequence of reversal operations that will sort the samples using the above algorithm. If there are more samples with the same height, their mutual order must be preserved: the one that was given first in the initial order must be placed before the others in the final order too.

  • 输入描述

    The input consists of several scenarios. Each scenario is described by two lines. The first line contains one integer number N , the number of samples, 1 ≤ N ≤ 100 000. The second line lists exactly N space-separated positive integers, they specify the heights of individual samples and their initial order.

    The last scenario is followed by a line containing zero.

  • 输出描述

    For each scenario, output one line with exactly N integers P1 , P1 , . . . PN ,separated by a space.
    Each Pi must be an integer (1 ≤ Pi ≤ N ) giving the position of the i-th sample just before the i-th reversal operation.

    Note that if a sample is already on its correct position Pi , you should output the number Pi anyway, indicating that the “interval between Pi and Pi ” (a single sample) should be reversed.

  • 参考代码

    
    

69. Looploop

  • 描述

    XXX gets a new toy named Looploop. The toy has N elements arranged in a loop, an arrow pointing to one of the elements, and two preset parameters k1 and k2. Every element has a number on it.

    The figure above shows a Looploop of 6 elments. Let’s assuming the preset parameter k1 is 3, and k2 is 4.
    XXX can do six operations with the toy.

    1: add x
    Starting from the arrow pointed element, add x to the number on the clockwise first k2 elements.

    2: reverse
    Starting from the arrow pointed element, reverse the first k1 clockwise elements.

    3: insert x
    Insert a new element with number x to the right (along clockwise) of the arrow pointed element.

    4: delete
    Delete the element the arrow pointed and then move the arrow to the right element.

    5: move x
    x can only be 1 or 2. If x = 1 , move the arrow to the left(along the counterclockwise) element, if x = 2 move the arrow to the right element.

    6: query
    Output the number on the arrow pointed element in one line.

    XXX wants to give answers to every query in a serial of operations.

  • 输入描述

    There are multiple test cases.
    For each test case the first line contains N,M,k1,k2(2≤k1<k2≤N≤105, M≤105) indicating the initial number of elements, the total number of operations XXX will do and the two preset parameters of the toy.
    Second line contains N integers ai(-104≤ai≤104) representing the N numbers on the elements in Looploop along clockwise direction. The arrow points to first element in input at the beginning.
    Then m lines follow, each line contains one of the six operations described above.
    It is guaranteed that the “x” in the “add”,“insert” and “move” operations is always integer and its absolute value ≤104. The number of elements will never be less than N during the operations.
    The input ends with a line of 0 0 0 0.

  • 输出描述

    For each test case, output case number in the first line(formatted as the sample output). Then for each query in the case, output the number on the arrow pointed element in a single line.

  • 参考代码

    
    

70. AVL Tree

  • 描述

    An AVL tree is a kind of balanced binary search tree. Named after their inventors, Adelson-Velskii and Landis, they were the first dynamically balanced trees to be proposed. Like red-black trees, they are not perfectly balanced, but pairs of sub-trees differ in height by at most 1, maintaining an O(logn) search time. Addition and deletion operations also take O(logn) time.
    Definition of an AVL tree
    An AVL tree is a binary search tree which has the following properties:

    1. The sub-trees of every node differ in height by at most one.
    2. Every sub-tree is an AVL tree.

    Balance requirement for an AVL tree: the left and right sub-trees differ by at most 1 in height.An AVL tree of n nodes can have different height.
    For example, n = 7:

    So the maximal height of the AVL Tree with 7 nodes is 3.
    Given n,the number of vertices, you are to calculate the maximal hight of the AVL tree with n nodes.

  • 输入描述

    Input file contains multiple test cases. Each line of the input is an integer n(0<n<=10^9).
    A line with a zero ends the input.

  • 输出描述

    An integer each line representing the maximal height of the AVL tree with n nodes.

  • 参考代码

    #include<iostream>
    #include<vector>
    using namespace std;
    
    vector<int> dp;
    
    int computeNodes(int h) {
    	if (h == 0)
    		return 1;
    	if (h == 1)
    		return 2;
    	if (dp[h] != -1)
    		return dp[h];
    	
    	dp[h] = 1 + computeNodes(h - 1) + computeNodes(h - 2);
    	return dp[h];
    }
    
    int main() {
    	int n;
    	dp.assign(100, -1);
    
    	while (cin >> n) {
    		if (n == 0) {
    			return 0;
    		}
    
    		int height = 0;
    		while (computeNodes(height) <= n) {
    			height++;
    		}
    		cout << height - 1 << endl;
    	}
    
    	return 0;
    }
    

71. Root of AVL Tree

  • 描述

    An AVL tree is a self-balancing binary search tree. In an AVL tree, the heights of the two child subtrees of any node differ by at most one; if at any time they differ by more than one, rebalancing is done to restore this property.

    Now given a sequence of insertions, you are supposed to tell the root of the resulting AVL tree.

  • 输入描述

    Each input file contains one test case. For each case, the first line contains a positive integer N (<=100) which is the total number of keys to be inserted. Then N distinct integer keys are given in the next line. All the numbers in a line are separated by a space.

  • 输出描述

    For each test case, print ythe root of the resulting AVL tree in one line.

  • 参考代码

    #include <iostream>
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    struct TreeNode {
        int key;
        int height;
        TreeNode* lchild;
        TreeNode* rchild;
        TreeNode(int k) : key(k), height(1), lchild(NULL), rchild(NULL) {}
    };
    
    int getHeight(TreeNode* node) {
        if (!node) {
            return 0;
        }
        return node->height;
    }
    
    int getBalance(TreeNode* node) {
        if (!node) {
            return 0;
        }
        return getHeight(node->lchild) - getHeight(node->rchild);
    }
    
    TreeNode* rightRotate(TreeNode* y) {
        TreeNode* x = y->lchild;
        TreeNode* T = x->rchild;
    
        x->rchild = y;
        y->lchild = T;
    
        y->height = 1 + max(getHeight(y->lchild), getHeight(y->rchild));
        x->height = 1 + max(getHeight(x->lchild), getHeight(x->rchild));
    
        return x;
    }
    
    TreeNode* leftRotate(TreeNode* x) {
        TreeNode* y = x->rchild;
        TreeNode* T = y->lchild;
    
        y->lchild = x;
        x->rchild = T;
    
        x->height = 1 + max(getHeight(x->lchild), getHeight(x->rchild));
        y->height = 1 + max(getHeight(y->lchild), getHeight(y->rchild));
    
        return y;
    }
    
    TreeNode* insert(TreeNode* root, int key) {
        if (!root) {
            return new TreeNode(key);
        }
    
        if (key < root->key) {
            root->lchild = insert(root->lchild, key);
        }
        else {
            root->rchild = insert(root->rchild, key);
        }
    
        root->height = 1 + max(getHeight(root->lchild), getHeight(root->rchild));
        int balance = getBalance(root);
    
        if (balance > 1 && key < root->lchild->key) {
            return rightRotate(root);
        }
    
        if (balance < -1 && key > root->rchild->key) {
            return leftRotate(root);
        }
    
        if (balance > 1 && key > root->lchild->key) {
            root->lchild = leftRotate(root->lchild);
            return rightRotate(root);
        }
    
        if (balance < -1 && key < root->rchild->key) {
            root->rchild = rightRotate(root->rchild);
            return leftRotate(root);
        }
    
        return root;
    }
    
    int getRoot(std::vector<int>& keys) {
        TreeNode* root = nullptr;
        for (int key : keys) {
            root = insert(root, key);
        }
        return root->key;
    }
    
    int main() {
        int n;
        cin >> n;
    
        vector<int> keys(n);
        for (int i = 0; i < n; ++i) {
            cin >> keys[i];
        }
    
        int rootKey = getRoot(keys);
        cout << rootKey << endl;
    
        return 0;
    }
    

72. B树的插入遍历和查找

  • 描述

    B树的属性
    1)所有的叶子都在同一层
    2)B树由一个最小度t定义
    3)所有的树节点出了根节点最少需要有t-1个关键字,根节点至少有一个关键字
    4)所有的节点包括根节点都至多包含2t-1个关键字
    5)节点的孩子数等于其关键字数+1
    6)所有节点内的数字增序排列。在关键字K1和K2之间的所有子树上的节点关键字值都在K1和K2之间
    7)B树的生长和收缩都是从根开始的,这点不同于其他的搜索树,其他的搜素树都是从底部开始生长和收缩的
    8)像其他平衡的二叉搜索树一样,搜索插入和删除的时间复杂度是O(logn)

    现在给一个代码模板,请按照注释要求实现B树插入、遍历和查询操作。

  • 输入描述

    每个测试用例第一行三个整数2<=t<=6, 15<=n<=100, 1<=m<=5。分别代表每个节点的最小度t,待插入数据的个数n,以及查询的个数m。

    第二行为n个整数,表示所有n个待插入的数据。每个数据都在[-10^9 , 10^9]范围。

    第三行m个整数,代表所有待查询的数据。

  • 输出描述

    首先需要遍历整颗B树,从小到大输出B树中所有元素,每个元素一行,一共n行。

    然后是m行,每行代表一个查找到的查询数据对应的B树节点的所有元素,从小到大输出,元素之间用空格隔开。

  • 参考代码

    
    

73. Frogs’ Neighborhood

  • 描述

    未名湖附近共有N个大小湖泊L1, L2, …, Ln(其中包括未名湖),每个湖泊Li里住着一只青蛙Fi(1 ≤ iN)。如果湖泊LiLj之间有水路相连,则青蛙FiFj互称为邻居。现在已知每只青蛙的邻居数目x1, x2, …, xn,请你给出每两个湖泊之间的相连关系。

  • 输入描述

    第一行是测试数据的组数T(0 ≤ T ≤ 20)。每组数据包括两行,第一行是整数N(2 < N < 10),第二行是N个整数,x1, x2,…, xn(0 ≤ xiN)。

  • 输出描述

    对输入的每组测试数据,如果不存在可能的相连关系,输出"NO"。否则输出"YES",并用N×N的矩阵表示湖泊间的相邻关系,即如果湖泊i与湖泊j之间有水路相连,则第i行的第j个数字为1,否则为0。每两个数字之间输出一个空格。如果存在多种可能,只需给出一种符合条件的情形。相邻两组测试数据之间输出一个空行。

  • 参考代码

    #include<iostream>
    #include<algorithm>
    using namespace std;
    
    struct Node {
        int degree;
        int id;
    };
    
    bool cmp(Node x, Node y) {
        return x.degree > y.degree;
    }
    
    int main() {
        int T;
        Node nodes[10];
        cin >> T;
    
        for (int i = 0; i < T; i++) {
            int n;
            bool flag = true;
            int map[10][10] = { 0 };
            cin >> n;
    
            for (int j = 0; j < n; j++) {
                int x;
                cin >> x;
                nodes[j].degree = x;
                nodes[j].id = j;
            }
            sort(nodes, nodes + n, cmp);
    
            for (int j = 0; j < n - 1; j++) {
                if (nodes[j].degree + j >= n) {
                    flag = false;
                    break;
                }
    
                for (int h = j + 1; h <= nodes[j].degree + j; h++) {
                    nodes[h].degree--;
                    if (nodes[h].degree < 0) {
                        flag = false;
                        break;
                    }
                    map[nodes[j].id][nodes[h].id] = 1;
                    map[nodes[h].id][nodes[j].id] = 1;
                }
    
                sort(nodes + j + 1, nodes + n, cmp);
            }
            if (nodes[n - 1].degree != 0) {
                flag = false;
            }
    
            if (flag) {
                cout << "YES" << endl;
                for (int x = 0; x < n; x++) {
                    cout << map[x][0];
                    for (int y = 1; y < n; y++) {
                        cout << " " << map[x][y];
                    }
                    cout << endl;
                }
            }
            else {
                cout << "NO" << endl;
            }
            cout << endl;
        }
    
        return 0;
    }
    

74. 邻接矩阵的使用

  • 描述

    这一节我们来复习下前面刚学的邻接矩阵的使用。给出一个包含有向图和无向图的混合图 G,图上有 n 个点和 m 条边,现在你需要使用邻接矩阵来存储该混合图 G 并按格式输出邻接矩阵。

  • 输入描述

    输入第一行为两个正整数 n 和 m(1≤n,m≤100),表示混合图上的 n 个点和 m 条边。接下来输入 m 行,每行输入三个整数 a,x,y(0≤x,y<n),表示点 x 和点 y 之间有一条边。如果 a=0,则表示该边为有向边,如果 a = 1,则表示该边为无向边。

  • 输出描述

    输出一个 n×n 的邻接矩阵,矩阵中第 i 行第 j 列的值描述了点 i 到点 j 的连边情况。如果值为 0 表示点 i 到点 j 没有边相连,值为 1 表示有边相连。在每一行中,每两个整数之间用一个空格隔开,最后一个整数后面没有空格。

  • 参考代码

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

75. 邻接表的使用

  • 描述

    这一节我们来复习下前面刚学的邻接表的使用。给出一个包含有向图和无向图的混合图 G,图上有 n 个点和 m 条边,现在你需要使用邻接表来存储该混合图 G 并按格式输出邻接表。

  • 输入描述

    输入第一行为两个正整数 n 和 m(1≤n,m≤100),表示混合图上的 n 个点和 m 条边。接下来输入 m 行,每行输入三个整数 a,x,y(0≤a≤1,0≤x,y<n),表示点 x 和点 y 之间有一条边。如果 a = 0,则表示该边为有向边,如果 a = 1,则表示该边为无向边。

  • 输出描述

    输出邻接表,输出 n 行,第 i 行表示第 i 个点连接边的情况,首先输出 i,接着输出:,然后输出所有点 i 能到达的点的编号,边关系中后出现的点先输出。每个整数前有一个空格,具体格式见样例。

  • 参考代码

    #include<iostream>
    #include<cstring>
    using namespace std;
    
    struct Edge {
        int to;
        int next;
    }edges[1000];
    
    int head[1000] = { -1 };
    int edgeCount = 0;
    
    void insert(int x, int y) {
        edges[edgeCount].to = y;
        edges[edgeCount].next = head[x];
        head[x] = edgeCount;
        edgeCount++;
    }
    
    int main() {
        int n, m;
        cin >> n >> m;
    
        for (int i = 0; i < m; i++) {
            int a, x, y;
            cin >> a >> x >> y;
            if (a == 0) {
                insert(x, y);
            }
            else {
                insert(x, y);
                insert(y, x);
            }
        }
    
        for (int i = 0; i < n; i++) {
            cout << i << ":";
            for (int j = head[i]; j != -1; j = edges[j].next) {
                cout << " " << edges[j].to;
            }
            cout << endl;
        }
    }
    

76. 找到小镇的法官

  • 描述

    小镇里有 n 个人,按从 1 到 n 的顺序编号。传言称,这些人中有一个暗地里是小镇法官。

    如果小镇法官真的存在,那么:

    1. 小镇法官不会信任任何人。
    2. 每个人(除了小镇法官)都信任这位小镇法官。

    只有一个人同时满足属性 1 和属性 2 。

    给你一个数组 trust ,其中 trust[i] = [ai, bi] 表示编号为 ai 的人信任编号为 bi 的人。

    如果小镇法官存在并且可以确定他的身份,请返回该法官的编号;否则,返回 -1 。

  • 输入描述

    第一行两个正整数1<=n<=1000, 0<=m<=104,分别表示人数和m对信任关系。
    接下来m行,每行两个整数1<=a, b<=n,且a!=b,保证m行互不相同。

  • 输出描述

    如果小镇法官存在并且可以确定他的身份,请返回该法官的编号;否则,返回 -1 。

  • 参考代码

    #include<iostream>
    #include<cstring>
    using namespace std;
    
    struct Edge {
        int trust;
        int next;
    };
    
    int head1[10000];
    int head2[10000];
    int edgeCount = 0;
    Edge edges[20000];
    
    void insert(int x, int y) {
        edges[edgeCount].trust = y;
        edges[edgeCount].next = head1[x];
        head1[x] = edgeCount;
        edgeCount++;
    
        edges[edgeCount].trust = x;
        edges[edgeCount].next = head2[y];
        head2[y] = edgeCount;
        edgeCount++;
    }
    
    int main() {
        int n, m;
        cin >> n >> m;
        memset(head1, -1, sizeof(head1));
        memset(head2, -1, sizeof(head2));
    
        for (int i = 0; i < m; i++) {
            int a, b;
            cin >> a >> b;
            insert(a, b);
        }
    
        for (int i = 1; i <= n; i++) {
            if (head1[i] == -1) {
                int count = 0;
                for (int j = head2[i]; j != -1; j = edges[j].next) {
                    count++;
                }
                if (count == n - 1) {
                    cout << i;
                    return 0;
                }
            }
        }
    
        cout << -1;
        return 0;
    }
    

77. 找出星型图的中心节点

  • 描述

    有一个无向的 星型 图,由 n 个编号从 1 到 n 的节点组成。星型图有一个 中心 节点,并且恰有 n - 1 条边将中心节点与其他每个节点连接起来。

    给你一个二维整数数组 edges ,其中 edges[i] = [ui, vi] 表示在节点 ui 和 vi 之间存在一条边。请你找出并返回 edges 所表示星型图的中心节点。

  • 输入描述

    每个测试有m+1行输入,其中第一行为两个整数3<=n<=100000, m=n-1,分别表示节点数和边的数量。

    后面m行,每行两个整数,1<=u,v<=n且u!=v

  • 输出描述

    中心节点号

  • 参考代码

    #include<iostream>
    using namespace std;
    
    int main() {
        int n, m;
        int count[100010] = { 0 };
        cin >> n >> m;
    
        for (int i = 0; i < m; i++) {
            int a, b;
            cin >> a >> b;
            count[a]++;
            count[b]++;
            if (count[a] == m) {
                cout << a;
                return 0;
            }
            if (count[b] == m) {
                cout << b;
                return 0;
            }
        }
        for (int i = 1; i <= n; i++) {
            if (count[i] == n - 1) {
                cout << i;
                return 0;
            }
        }
    
        return 0;
    }
    

78. 修建大桥

  • 描述

    小明来到一个由 n 个小岛组成的世界,岛与岛之间通过修建桥,来让岛上的居民可以去其他的小岛。已知已经修建了 m 座桥,居民们想让蒜头君帮忙计算,最少还要再修建几座桥,居民们才能去所有的岛。

  • 输入描述

    第一行输入两个数字 n,m( 1 ≤ n ≤ 1000, 0 ≤ m ≤ n×(n−1)/2 ),分别代表岛的个数,和已经修建的桥的个数,岛的编号分别是 1…n。接下来的 m 行,每行两个数字,代表这两个编号的岛之间已经有一座桥了。

  • 输出描述

    输出最少还需要修建多少座桥,居民才能去所有的岛。

  • 参考代码

    #include<iostream>
    #include<cstring>
    #include<vector>
    using namespace std;
    
    vector<int> graph[1000];
    bool vst[1000];
    
    void dfs(int node) {
    	vst[node] = true;
    
    	for (int i = 0; i < graph[node].size(); i++) {
    		int nextNode = graph[node][i];
    
    		if (!vst[nextNode]) {
    			dfs(nextNode);
    		}
    	}
    }
    
    int main() {
    	int n, m;
    	memset(vst, false, sizeof(vst));
    	cin >> n >> m;
    
    	for (int i = 0; i < m; i++) {
    		int a, b;
    		cin >> a >> b;
    		graph[a].push_back(b);
    		graph[b].push_back(a);
    	}
    
    	int components = 0;
    	for (int i = 1; i <= n; i++) {
    		if (!vst[i]) {
    			components++;
    			dfs(i);
    		}
    	}
    	cout << components - 1;
    
    	return 0;
    }
    

79. 最短路简化版

  • 描述

    经历一周忙碌的工作后,蒜头君想趁着周末好好游玩一番。蒜头君想去好多好多地方,他想去南锣鼓巷吃各种好吃的,想去颐和园滑冰,还想去怀柔滑雪场滑雪……可是时间有限,蒜头君并不能玩遍所有的地方,最后他决定去几个离他较近的。

    我们知道蒜头君一共想去 N 个地方玩耍,编号从 1 到 N,并且知道了蒜头君所在地方的编号 C,以及 M 条路径。现在蒜头君想让你帮他算一算,他到每个地方分别需要经过多少个地方?

  • 输入描述

    第一行输入三个正整数 N, M, C。代表蒜头君想去 N 个地方,有 M 条路径,蒜头君在编号为 C 的地方。1 ≤ N,C ≤ 1000, 1 ≤ C ≤ N, 1 ≤ M ≤ 10000。

    保证没有重复边,且图中所有点互相连通。

  • 输出描述

    输出 N 行,按编号从小到大,输出结果。第 i 行表示蒜头君到编号为 i 的地方,需要经过多少个地方。

  • 参考代码

    #include<iostream>
    #include<queue>
    using namespace std;
    
    vector<int> graph[1000];
    vector<int> dist(1000, -1);
    
    void shortestPath(int start) {
    	queue<int> nodes;
    	nodes.push(start);
    	dist[start] = 0;
    
    	while (!nodes.empty()) {
    		int cur = nodes.front();
    		nodes.pop();
    
    		for (int i = 0; i < graph[cur].size(); i++) {
    			int next = graph[cur][i];
    
    			if (dist[next] == -1) {
    				nodes.push(next);
    				dist[next] = dist[cur] + 1;
    			}
    		}
    	}
    }
    
    int main() {
    	int N, M, C;
    	cin >> N >> M >> C;
    
    	for (int i = 0; i < M; i++) {
    		int a, b;
    		cin >> a >> b;
    		graph[a].push_back(b);
    		graph[b].push_back(a);
    	}
    
    	shortestPath(C);
    
    	for (int i = 1; i <= N; i++) {
    		cout << dist[i] << endl;
    	}
    
    	return 0;
    }
    

80. 互粉攻略

  • 描述

    小明和他的同事们最近在玩一个好玩的游戏:互粉攻略。一共有 N 个人参加游戏,编号从 0 到 N−1, 游戏前每个人都会展示自己最靓丽的一面。当游戏开始时,每个人可以选择去关注别人。当 A 关注了 B,则 A 就成了 B 的粉丝,但是并不意味着 B 同时关注了 A。当所有人都选好后,游戏结束,人气指数最高的人成为冠军。小明制定了奇怪的规定:一个人的人气指数等于他的粉丝数减去关注数,因为小明觉得人气高的人,往往有很多粉丝,并且一般都非常高冷,很少去关注别人。
    小明发现一共有 M 条关注,粗心的他在统计时出了点小问题,所以可能会出现重复的关注。现在小明想知道每个人的人气指数,聪明的你能帮帮他么?

  • 输入描述

    第一行输入两个数 n 和 m,1 ≤ n ≤ 1000,1 ≤ m ≤ 100000。接下来输入 m 行,每行输入两个数 a 和 b,表示编号 a 的人关注了编号 b 的人,0 ≤ a,b ≤ n−1 ,a ≠ b。

  • 输出描述

    输出 N 行,每行输出每个人的人气指数,按编号依次输出即可。

  • 参考代码

    #include<iostream>
    #include<cstring>
    #include<vector>
    using namespace std;
    
    vector<vector<int>> graph1(1000);
    vector<vector<int>> graph2(1000);
    
    int main() {
        int n, m;
        cin >> n >> m;
        for (int i = 0; i < m; i++) {
            int a, b;
            cin >> a >> b;
            graph1[a].push_back(b);
            graph2[b].push_back(a);
        }
        for (int i = 0; i < n; i++) {
            cout << (int)graph2[i].size() - (int)graph1[i].size() << endl;
        }
        return 0;
    }
    

81. 网络延时

  • 描述

    某计算机网络中存在 n 个路由,每个路由代表一个子网。路由之间有 n−1 条互通关系,使得这 n 个网络之间任意两个网络都可以直接联通,或者通过其他网络间接连通。
    为了测试组建的网路的性能,假设相连的路由之间的数据传输需要一单位时间,现在需要知道任意两个路由之间传输数据最多需要多长时间。

  • 输入描述

    第一行一个整数 n ( 2 ≤ n ≤ 50000 ) 表示网络中路由个数。接下来 n−1 行,每行输入 u, v ( 1 ≤ u, v ≤ n ) ,表示路由 u, v 相连。

  • 输出描述

    输出一行表示答案。

  • 参考代码

    #include<iostream>
    #include<queue>
    #include<cstring>
    using namespace std;
    
    vector<int> graph[1000];
    int dist[1000];
    
    void shortestPath(int start) {
    	memset(dist, -1, sizeof(dist));
    	queue<int> nodes;
    	nodes.push(start);
    	dist[start] = 0;
    
    	while (!nodes.empty()) {
    		int cur = nodes.front();
    		nodes.pop();
    
    		for (int i = 0; i < graph[cur].size(); i++) {
    			int next = graph[cur][i];
    
    			if (dist[next] == -1) {
    				nodes.push(next);
    				dist[next] = dist[cur] + 1;
    			}
    		}
    	}
    }
    
    int main() {
    	int N;
    	int maxNode;
    	int maxPath = 0;
    	cin >> N;
    
    	for (int i = 0; i < N - 1; i++) {
    		int a, b;
    		cin >> a >> b;
    		graph[a].push_back(b);
    		graph[b].push_back(a);
    	}
    
    	shortestPath(1);
    	for (int i = 1; i <= N; i++) {
    		if (dist[i] > maxPath) {
    			maxPath = dist[i];
    			maxNode = i;
    		}
    	}
    	shortestPath(maxNode);
    	for (int i = 1; i <= N; i++) {
    		if (dist[i] > maxPath) {
    			maxPath = dist[i];
    		}
    	}
    
    	cout << maxPath << endl;
    
    	return 0;
    }
    

82. 圣诞树

  • 描述

    圣诞节快到了,小明准备做一棵大圣诞树。

    这棵树被表示成一组被编号的结点和一些边的集合,树的结点从 1 到 n 编号,树的根永远是 1。每个结点都有一个自身特有的数值,称为它的权重,各个结点的权重可能不同。对于一棵做完的树来说,每条边都有一个价值 ve,若设这条边 e 连接结点 i 和结点 j,且 i 为 j的父结点(根是最老的祖先),则该边的价值ve=sj*we,sj表示结点 j 的所有子孙及它自己的权重之和,we表示边 e 的权值。

    现在小明想造一棵树,他有 m 条边可以选择,使得树上所有边的总价值最小,并且所有的点都在树上,因为小明喜欢大树。

  • 输入描述

    第一行输入两个整数 n 和 m(0≤n,m≤50,000),表示结点总数和可供选择的边数。
    接下来输入一行,输入 n 个整数,依次表示每个结点的权重。
    接下来输入 m 行,每行输入 3 个正整数a,b,c(1≤a,b,≤n,1≤c≤10,000),表示结点 a 和结点 b 之间有一条权值为 c 的边可供造树选择。

  • 输出描述

    输出一行,如果构造不出这样的树,请输出No Answer,否则输出一个整数,表示造树的最小价值。

  • 参考代码

    
    

83. 骑车比赛

  • 描述

    蒜头君准备去参加骑车比赛,比赛在 n 个城市间进行,编号从 1 到 n 。选手们都从城市 1 出发,终点在城市 n 。

    已知城市间有 m 条道路,每条道路连接两个城市,注意道路是双向的。现在蒜头君知道了他经过每条道路需要花费的时间,他想请你帮他计算一下,他这次比赛最少需要花多少时间完成。

  • 输入描述

    第一行输入两个整数 n, m ( 1 ≤ n ≤ 1000, 1 ≤ m ≤ 5000 ),分别代表城市个数和道路总数。接下来输入 m 行,每行输入三个数字 a, b, c ( 1 ≤ a,b ≤ n, 1 ≤ c ≤ 200 ),分别代表道路的起点和道路的终点,以及蒜头君骑车通过这条道路需要花费的时间。保证输入的图是连通的。

  • 输出描述

    输出一行,输出一个整数,输出蒜头君完成比赛需要的最少时间。

  • 参考代码

    #include<iostream>
    #include<vector>
    #include<queue>
    using namespace std;
    
    struct Edge {
    	int time;
    	int arrival;
    	Edge(int time, int arrival) : time(time), arrival(arrival) {}
    };
    
    vector<Edge> gragh[1000];
    int dist[1000];
    
    void dijkstra(int start) {
    	queue<pair<int, int>> nodes;
    	nodes.push({ 0, start });
    	dist[start] = 0;
    
    	while (!nodes.empty()) {
    		int tmpDist = nodes.front().first;
    		int tmpArrival = nodes.front().second;
    		nodes.pop();
    
    		if (tmpDist > dist[tmpArrival]) {
    			continue;
    		}
    
    		for (int i = 0; i < gragh[tmpArrival].size(); i++) {
    			int nextArrival = gragh[tmpArrival][i].arrival;
    			int nextTime = gragh[tmpArrival][i].time;
    
    			if (nextTime + dist[tmpArrival] < dist[nextArrival]) {
    				dist[nextArrival] = nextTime + dist[tmpArrival];
    				nodes.push({ dist[nextArrival], nextArrival });
    			}
    		}
    	}
    }
    
    int main() {
    	int n, m;
    	memset(dist, 200000, sizeof(dist));
    	cin >> n >> m;
    
    	for (int i = 0; i < m; ++i) {
    		int a, b, c;
    		cin >> a >> b >> c;
    		Edge edge1(c, b);
    		Edge edge2(c, a);
    		gragh[a].push_back(edge1);
    		gragh[b].push_back(edge2);
    	}
    
    	dijkstra(1);
    
    	cout << dist[n] << endl;
    
    	return 0;
    }
    

84. 迷阵突围

  • 描述

    小明陷入了坐标系上的一个迷阵,迷阵上有 n 个点,编号从 1 到 n 。小明在编号为 1 的位置,他想到编号为 n 的位置上。小明当然想尽快到达目的地,但是他觉得最短的路径可能有风险,所以他会选择第二短的路径。现在小明知道了 n 个点的坐标,以及哪些点之间是相连的,他想知道第二短的路径长度是多少。

    注意,每条路径上不能重复经过同一个点。

  • 输入描述

    第一行输入两个整数 n ( 1 ≤ n ≤ 200 ) 和 m ,表示一共有 n 个点和 m 条边。
    接下来输入 n 行,每行输入两个整数 xi , yi ( −500 ≤ xi,yi ≤ 500 ),代表第 i 个点的坐标。
    接下来输入 m 行,每行输入两个整数 pj , qj ( 1 ≤ pj,qj ≤ n ),表示点 pj 和点 qj 之间相连。

  • 输出描述

    输出一行,输出包含一个数,表示第二短的路径长度(小数点后面保留两位),如果第一短路径有多条,则答案就是第一最短路径的长度;如果第二最短路径不存在,则输出 −1 。

  • 参考代码

    
    

85. 闯关游戏

  • 描述

    蒜头君在玩一个很好玩的游戏,这个游戏一共有至多 100 个地图,其中地图 1 是起点,房间 n 是终点。有的地图是补给站,可以加 ki 点体力,而有的地图里存在怪物,需要消耗 ki 点体力,地图与地图之间存在一些单向通道链接。

    蒜头君从 1 号地图出发,有 100 点初始体力。每进入一个地图的时候,需要扣除或者增加相应的体力值。这个过程持续到走到终点,或者体力值归零就会 Game Over 。不过,他可以经过同个地图任意次,且每次都需要接受该地图的体力值。

  • 输入描述

    第 1 行一个整数 n ( n ≤ 100 )。
    第 2 ~ n+1 行,每行第一个整数表示该地图体力值变化。接下来是从该房间能到达的房间名单,第一个整数表示房间数,后面是能到达的房间编号。

  • 输出描述

    若玩家能到达终点,输出 Yes ,否则输出 No 。

  • 参考代码

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    vector<vector<int>> adj; 
    vector<int> health; 
    vector<bool> visited; 
    
    bool canReachDestination(int currentRoom, int currentHealth) {
        if (currentRoom == adj.size() - 1) {
            return true;
        }
    
        if (currentHealth <= 0) {
            return false;
        }
    
        visited[currentRoom] = true;
    
        for (int nextRoom : adj[currentRoom]) {
            if (!visited[nextRoom]) {
                int newHealth = currentHealth + health[nextRoom];
                if (canReachDestination(nextRoom, newHealth)) {
                    return true;
                }
            }
        }
    
        return false;
    }
    
    int main() {
        int n;
        cin >> n;
    
        adj.resize(n + 1);
        health.resize(n + 1);
        visited.resize(n + 1, false);
    
        for (int i = 1; i <= n; ++i) {
            cin >> health[i];
            int numRooms;
            cin >> numRooms;
            for (int j = 0; j < numRooms; ++j) {
                int room;
                cin >> room;
                adj[i].push_back(room);
            }
        }
    
        if (canReachDestination(1, 100)) {
            cout << "Yes" << endl;
        } else {
            cout << "No" << endl;
        }
    
        return 0;
    }
    

86. 小明的训练室

  • 描述

    小明的训练室有 N 个站点,另外有 M 条单向边连接这些站点。第 i 条路从 Si 站到 Ei 站,有高度为 Hi 的围栏,小明是需要跳跃的。
    现在小明有 T 个任务要完成。第 i 个任务,小明要从 Ai 站到 Bi 站,小明想要路径中最高围栏尽可能小。请你确定这个高度。

  • 输入描述

    第一行输入三个整数 N, M, T ( 1 ≤ N ≤ 300 , 1 ≤ M ≤ 25000 , 1 ≤ T ≤ 40000 ) 。接下来 M 行,每行三个整数 Si, Ei, Hi ( 1 ≤ Si,Ei ≤ N , 1 ≤ Hi ≤ 106 ) 。再接下来 T 行,每行两个整数 Ai, Bi ( 1 ≤ Ai,Bi ≤ N ) 。

  • 输出描述

    对于每个询问,输出路径中最高围栏高度的最小值。若无法到达,则输出 −1 。

  • 参考代码

    #include <iostream>
    #include <vector>
    #include <queue>
    #include <limits>
    using namespace std;
    
    const int INF = 1e9;
    
    struct Edge {
        int to;
        int height;
    };
    
    void dijkstra(int start, int n, const vector<vector<Edge>>& graph, vector<int>& min_fence_height) {
        priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
        pq.push({0, start});
        min_fence_height[start] = 0;
        
        while (!pq.empty()) {
            int u = pq.top().second;
            int min_height = pq.top().first;
            pq.pop();
            
            if (min_height > min_fence_height[u])
                continue;
            
            for (const auto& edge : graph[u]) {
                int v = edge.to;
                int height = edge.height;
                
                if (min_fence_height[v] > max(min_fence_height[u], height)) {
                    min_fence_height[v] = max(min_fence_height[u], height);
                    pq.push({min_fence_height[v], v});
                }
            }
        }
    }
    
    int main() {
        int N, M, T;
        cin >> N >> M >> T;
        
        vector<vector<Edge>> graph(N + 1);
        
        for (int i = 0; i < M; ++i) {
            int S, E, H;
            cin >> S >> E >> H;
            graph[S].push_back({E, H});
        }
        
        for (int i = 0; i < T; ++i) {
            int A, B;
            cin >> A >> B;
            
            vector<int> min_fence_height(N + 1, INF);
            
            dijkstra(A, N, graph, min_fence_height);
            
            int result = min_fence_height[B];
            
            if (result == INF)
                cout << "-1" << endl;
            else
                cout << result << endl;
        }
        
        return 0;
    }
    

87. 节点的最近公共祖先

  • 描述

    给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。

  • 输入描述

    第一行包含三个正整数 3<=N,M<=10000,S 分别表示树的结点个数、询问的个数和树根结点的序号。

    接下来 N-1 行每行包含两个正整数 x, y表示 x 结点和 y 结点之间有一条直接连接的边(数据保证可以构成树,x为父节点)。

    接下来 M 行每行包含两个正整数 a, b 表示询问 a 结点和 b 结点的最近公共祖先。不保证a!=b

  • 输出描述

    输出包含 M 行,每行包含一个正整数,依次为每一个询问的结果。

  • 参考代码

    #include <iostream>
    #include <vector>
    #include <cmath>
    using namespace std;
    
    const int MAXN = 10001;
    const int MAXLOG = 15;
    
    vector<int> adj[MAXN];
    int parent[MAXN];
    int depth[MAXN];
    int ancestor[MAXN][MAXLOG];
    
    void dfs(int node, int par, int dep) {
        parent[node] = par;
        depth[node] = dep;
        for (int i = 0; i < adj[node].size(); ++i) {
            int child = adj[node][i];
            if (child != par) {
                dfs(child, node, dep + 1);
            }
        }
    }
    
    void binaryLift(int N) {
        for (int i = 1; i <= N; ++i) {
            ancestor[i][0] = parent[i];
        }
        
        for (int j = 1; (1 << j) <= N; ++j) {
            for (int i = 1; i <= N; ++i) {
                if (ancestor[i][j - 1] != -1) {
                    ancestor[i][j] = ancestor[ancestor[i][j - 1]][j - 1];
                }
            }
        }
    }
    
    int findLCA(int u, int v) {
        if (depth[u] < depth[v]) swap(u, v);
        
        int log;
        for (log = 1; (1 << log) <= depth[u]; ++log);
        log--;
        
        for (int i = log; i >= 0; --i) {
            if (depth[u] - (1 << i) >= depth[v]) {
                u = ancestor[u][i];
            }
        }
        
        if (u == v) return u;
        
        for (int i = log; i >= 0; --i) {
            if (ancestor[u][i] != -1 && ancestor[u][i] != ancestor[v][i]) {
                u = ancestor[u][i];
                v = ancestor[v][i];
            }
        }
        
        return parent[u];
    }
    
    int main() {
        int N, M, S;
        cin >> N >> M >> S;
        
        for (int i = 1; i <= N; ++i) {
            adj[i].clear();
            parent[i] = -1;
            depth[i] = 0;
            for (int j = 0; j < MAXLOG; ++j) {
                ancestor[i][j] = -1;
            }
        }
        
        for (int i = 1; i < N; ++i) {
            int x, y;
            cin >> x >> y;
            adj[x].push_back(y);
            adj[y].push_back(x);
        }
        
        dfs(S, -1, 0);
        binaryLift(N);
        
        for (int i = 0; i < M; ++i) {
            int a, b;
            cin >> a >> b;
            int lca = findLCA(a, b);
            cout << lca << endl;
        }
        
        return 0;
    }
    

88. 布设光纤

  • 描述

    S 国有 n 座基站,现在小明想给基站之间布设光纤,使得任意两座基站都是连通的,光纤传输具有传递性,即如果基站 A 和基站 B 之间有光纤,基站 B 和基站 C 之间有光纤,则基站 A 和基站 C 也是连通的,可以通过中间基站 B 来完成传输。
    不同的基站之间布设光纤的费用是不同的,现在小明知道了任意两座基站之间布设光纤的费用,求问如何布设,可以使得任意两座基站都是连通的,且总费用最小。

  • 输入描述

    第一行输入一个整数 n ( 2 ≤ n ≤ 100 ) ,表示基站总数。
    接下来输入 n × n 的矩阵。第 i 行第 j 列的整数表示第 i 座基站和第 j 座基站之间布设光纤的费用 wij ( 0 ≤ wij ≤ 10000 ) 。

  • 输出描述

    输出一个整数,表示布设光纤的最小总费用,且使任意两座基站都是连通的。

  • 参考代码

    
    

89. 连线问题

  • 描述

    小明和花椰菜君经常出难题考对方。一天,花椰菜君给小明出了这样一道难题:花椰菜君在坐标系上随机画了 N 个点,然后让小明给点之间连线,要求任意两点之间都是连通的,且所连的线段长度之和最小。聪明的你快来帮小明解决一下吧。

  • 输入描述

    第一行输入一个整数 N ( 1 ≤ N ≤ 100 ) ,表示花椰菜君一共画了 N 个点。然后输入 N 行,每行输入两个整数 x, y ( 0 ≤ x,y ≤ 1000 ) ,代表一个点的坐标。

  • 输出描述

    输出一行,输出一个实数,表示所连线段长度之和的最小值(结果四舍五入保留两位小数)。

  • 参考代码

    #include <iostream>
    #include <vector>
    #include <cmath>
    #include <iomanip>
    using namespace std;
    
    const int MAXN = 105;
    
    double computeDistance(int x1, int y1, int x2, int y2) {
        return sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
    }
    
    double prim(vector<pair<int, int>>& points, int n) {
        vector<bool> visited(n, false);
        vector<double> minDist(n, INFINITY);
        double totalCost = 0.0;
    
        minDist[0] = 0.0;
    
        for (int count = 0; count < n; ++count) {
            int u = -1;
    
            for (int i = 0; i < n; ++i) {
                if (!visited[i] && (u == -1 || minDist[i] < minDist[u])) {
                    u = i;
                }
            }
    
            visited[u] = true;
            totalCost += minDist[u];
    
            for (int v = 0; v < n; ++v) {
                if (!visited[v]) {
                    double dist = computeDistance(points[u].first, points[u].second,
                                                  points[v].first, points[v].second);
                    if (dist < minDist[v]) {
                        minDist[v] = dist;
                    }
                }
            }
        }
    
        return totalCost;
    }
    
    int main() {
        int n;
        cin >> n;
    
        vector<pair<int, int>> points(n);
        for (int i = 0; i < n; ++i) {
            cin >> points[i].first >> points[i].second;
        }
    
        double minCost = prim(points, n);
    
        cout << fixed << setprecision(2) << minCost << endl;
    
        return 0;
    }
    

90. 威虎山上的分配

  • 描述

    每年过年的时候,座山雕都会给兄弟们分银子,分银子之前,座山雕允许大伙儿发表意见,因为要是没法满足所有人的意见,指不定谁要搞出什么大新闻。不过每个人在提意见的时候只能说:“我认为 A 分的银子应该比 B 多!”。座山雕决定要找出一种分配方案,满足所有人的意见,同时使得所有人分得的银子总数最少,并且每个人分得的银子最少为 100 两。

  • 输入描述

    第一行两个整数 n , m ( 0 < n ≤ 10000 , 0 < m ≤ 20000 ) ,表示总人数和总意见数;
    以下 m 行,每行两个整数 a 、b ,之间用一个空格隔开,表示某个意见认为第 a 号小弟所分得的银两应该比第 b 号小弟多,所有小弟的编号由 1 开始。

  • 输出描述

    若无法找到合法方案,则输出 “Unhappy!” (不包含引号),否则输出一个数表示最少总银两数。

  • 参考代码

    
    

91. 判定欧拉回路

  • 描述

    你学过一笔画问题么?其实一笔画问题又叫欧拉回路,是指在画的过程中,笔不离开纸,且图中每条边仅画一次,而且可以回到起点的一条回路。
    蒜头君打算考考你,给你一个图,问是否存在欧拉回路?

  • 输入描述

    第 1 行输入两个正整数,分别是节点数 N ( 1 < N < 1000 ) 和边数 M ( 1 < M < 100000 ) ;紧接着 M 行对应 M 条边,每行给出一对正整数,分别是该条边直接连通的两个节点的编号(节点从 1 到 N 编号)。

  • 输出描述

    若存在欧拉回路则输出 1 ,否则输出 0 。

  • 参考代码

    #include <iostream>
    #include <vector>
    #include <cstring>
    using namespace std;
    
    const int MAXN = 1001;
    
    vector<int> adj[MAXN];
    int degree[MAXN];     
    bool visited[MAXN];   
    
    void dfs(int v) {
        visited[v] = true;
        for (int u : adj[v]) {
            if (!visited[u]) {
                dfs(u);
            }
        }
    }
    
    bool isConnected(int N) {
        memset(visited, false, sizeof(visited));
        int start = -1;
        for (int i = 1; i <= N; ++i) {
            if (degree[i] > 0) {
                start = i;
                break;
            }
        }
        if (start == -1) return true; 
        dfs(start);
        
        for (int i = 1; i <= N; ++i) {
            if (degree[i] > 0 && !visited[i]) {
                return false;
            }
        }
        return true;
    }
    
    int main() {
        int N, M;
        cin >> N >> M;
        
        for (int i = 0; i < M; ++i) {
            int u, v;
            cin >> u >> v;
            adj[u].push_back(v);
            adj[v].push_back(u);
            degree[u]++;
            degree[v]++;
        }
        
        if (!isConnected(N)) {
            cout << 0 << endl;
            return 0;
        }
    
        for (int i = 1; i <= N; ++i) {
            if (degree[i] % 2 != 0) {
                cout << 0 << endl;
                return 0;
            }
        }
    
        cout << 1 << endl;
        
        return 0;
    }
    

92. 商业信息共享

  • 描述

    有 N 个公司,从每个公司都能单向地向另外一个公司分享最新商业信息,因为他们之间有着某种合作,你需要解决两个问题:

    • 现在有一个最新的商业信息,至少需要告诉多少个公司,使得所有的公司最终都能得到该信息。
    • 在原有基础上,至少需要再让多少对公司建立这种合作,使任意一个公司获得某个最新商业信息后,经过若干次分享,所有的公司最终都能得到该信息。
  • 输入描述

    第一行输入一个整数 N ( 1 ≤ N ≤ 100 ) 。
    接下来 N 行,每行若干个整数,表示第 i 个公司可以向哪些公司分享信息,以 0 结束。

  • 输出描述

    输出共两行,每行一个整数,分别表示问题 1 和问题 2 的答案。

  • 参考代码

    
    
  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值