2021财院ACM选拔赛题解

本题解只是参考答案,思路并不唯一!!!

A题 存款利息

方法一

本题主要考察循环语句,题目较为简单,直接看代码

C语言


#include <stdio.h>
int main()
{
	double money_1,money_2;
	scanf("%lf", &money_1);
	money_2 = money_1;
	money_1 += money_1 * 4 * 0.0275; //存四年,复利计息
	for (int i = 0; i < 4; ++i)
	{
		money_2 += money_2 * 0.0175; //加上一年的利息,单利计息
	}
	printf("%.2lf %.2lf", money_1,money_2);
	return 0;
}

B题 判断闰年

方法一

本题主要考察if判断,要掌握闰年的条件。
闰年条件:

  1. 能被4整除但不能被100整除
  2. 能被400整除

C语言

#include <stdio.h>
int main()
{
	int year, mon, day;
	scanf("%d-%d-%d", &year, &mon, &day);
	if(year % 400 == 0 || year % 4 == 0 && year % 100 != 0) //闰年条件
	{
		printf("%s", "yes");
	}
	else
	{
		printf("%s", "no");
	}
	return 0;
}

C题 acb模式

题目来源:132-pattern

前言

由于本题中 n n n 的最大值可以到 1 0 4 10^4 104
,因此对于一个满足 a c b acb acb 模式的三元组下标 ( i , j , k ) (i, j, k) (i,j,k),枚举其中的 2 个下标时间复杂度为 O ( n 2 ) O(n^2) O(n2)
,很容易超出时间限制。

因此我们可以考虑枚举其中的 1 1 1 个下标,并使用合适的数据结构维护另外的 2 2 2 个下标的可能值。

方法一 枚举 c

枚举 c c c 是容易想到并且也是最容易实现的。由于 c c c 是模式中的最大值,并且其出现在 a a a b b b 的中间,因此我们只需要从左到右枚举 c c c 的下标 j j j,那么:

由于 a a a 是模式中的最小值,因此我们在枚举 j j j 的同时,维护数组 a 中左侧元素 a [ 0.. j − 1 ] a[0..j-1] a[0..j1] 的最小值,即为 a a a 对应的元素 a [ i ] a[i] a[i]。需要注意的是,只有 a [ i ] < a [ j ] a[i] < a[j] a[i]<a[j] 时, a [ i ] a[i] a[i] 才能作为 a 对应的元素;

由于 b b b 是模式中的次小值,因此我们可以使用一个有序集合(例如平衡树)维护数组 a a a 中右侧元素 a [ j + 1.. n − 1 ] a[j+1..n-1] a[j+1..n1] 中的所有值。当我们确定了 a [ i ] a[i] a[i] a [ j ] a[j] a[j] 之后,只需要在有序集合中查询严格比 a [ i ] a[i] a[i] 大的那个最小的元素,即为 a [ k ] a[k] a[k]。需要注意的是,只有 $a[k] < a[j] 时 , 时, a[k]$ 才能作为 c c c 对应的元素。

C++

#include<iostream>
#include <set>
#include <vector>
using namespace std;
class Solution {
public:
    bool find132pattern(vector<int>& nums) {
        int n = nums.size();
        if (n < 3) {
            return false;
        }
        // 左侧最小值
        int left_min = nums[0];
        // 右侧所有元素
        multiset<int> right_all;
        for (int k = 2; k < n; ++k) {
            right_all.insert(nums[k]);
        }
        for (int j = 1; j < n - 1; ++j) {
            if (left_min < nums[j]) {
                auto it = right_all.upper_bound(left_min);
                if (it != right_all.end() && *it < nums[j]) {
                    return true;
                }
            }
            left_min = min(left_min, nums[j]);
            right_all.erase(right_all.find(nums[j + 1]));
        }
        return false;
    }
};
int main()
{
    Solution sol;
    int n;
    cin >> n;
    vector<int> array(n);
    for (int i = 0; i < n; ++i){
        cin >> array[i];
    }
    bool res = sol.find132pattern(array);
	if(res){
        cout << "True";
	}
    else{
        cout << "False";
    }
    return 0;
}

Java

import java.io.*;
import java.util.*;
import java.util.TreeMap;
class Solution {
    public boolean find132pattern(int[] nums) {
        // a < c < b
        int n = nums.length;
        if (n < 3) return false;
        TreeMap<Integer, Integer> rightNums = new TreeMap<>();//储存当前位置右边的 <数值,个数> c
        int leftMin = nums[0]; // 左侧最小值 a
        for (int i = 2; i < n; i++) {
            rightNums.put(nums[i], rightNums.getOrDefault(nums[i], 0) + 1);
        }
        for (int i = 1; i < n - 1; i++) {
            //检查
            if (leftMin < nums[i]) { // a<b
                Integer c = rightNums.ceilingKey(leftMin + 1); //获得比a大的最小元素 c>=a+1
                if (c != null && c < nums[i]) { //c<b
                    return true; //符合情况
                }
            }
            //维护结构
            rightNums.put(nums[i + 1], rightNums.get(nums[i + 1]) - 1);
            if (rightNums.get(nums[i + 1]) == 0) rightNums.remove(nums[i + 1]);
            leftMin = Math.min(leftMin, nums[i]);
        }
        return false;
    }
}
public class Main {
    public static void main(String args[]) throws Exception {
        Scanner cin=new Scanner(System.in);
        int n = cin.nextInt();
        int[] arr = new int[n];
        for(int i =0;i<n;++i){
            arr[i]=cin.nextInt();
        }
        Solution sol = new Solution();
        if(sol.find132pattern(arr)){
            System.out.print("True");
        }else{
            System.out.print("False");
        }
    }
}

方法二 枚举a

如果我们从左到右枚举 a a a 的下标 i i i,那么 j , k j, k j,k 的下标范围都是减少的,这样就不利于对它们进行维护。因此我们可以考虑从右到左枚举 i i i

那么我们应该如何维护 j , k j, k j,k 呢?在 a c b acb acb 模式中,如果 a < b a<b a<b 并且 b < c b<c b<c,那么根据传递性, a < c a<c a<c 也是成立的,那么我们可以使用下面的方法进行维护:

  • 我们使用一种数据结构维护所有遍历过的元素,它们作为 b b b 的候选元素。每当我们遍历到一个新的元素时,就将其加入数据结构中;

  • 在遍历到一个新的元素的同时,我们可以考虑其是否可以作为 c c c。如果它作为 c c c,那么数据结构中所有严格小于它的元素都可以作为 b b b,我们将这些元素全部从数据结构中移除,并且使用一个变量维护所有被移除的元素的最大值。这些被移除的元素都是可以真正作为 b b b 的,并且元素的值越大,那么我们之后找到 a a a 的机会也就越大。

那么这个「数据结构」是什么样的数据结构呢?我们尝试提取出它进行的操作:

  • 它需要支持添加一个元素;

  • 它需要支持移除所有严格小于给定阈值的所有元素;

  • 上面两步操作是「依次进行」的,即我们先用给定的阈值移除元素,再将该阈值加入数据结构中。

这就是「单调栈」。在单调栈中,从栈底到栈顶的元素是严格单调递减的。当给定阈值 x x x 时,我们只需要不断地弹出栈顶的元素,直到栈为空或者 x x x 严格小于栈顶元素。此时我们再将 x x x 入栈,这样就维护了栈的单调性。

因此,我们可以使用单调栈作为维护 b b b 的数据结构,并给出下面的算法:

  • 我们用单调栈维护所有可以作为 b b b 的候选元素。初始时,单调栈中只有唯一的元素 a [ n − 1 ] \textit{a}[n-1] a[n1]。我们还需要使用一个变量 max_k \textit{max\_k} max_k 记录所有可以真正作为 b b b 的元素的最大值;

  • 随后我们从 n − 2 n-2 n2 开始从右到左枚举元素 a [ i ] a[i] a[i]

    • 首先我们判断 a [ i ] a[i] a[i] 是否可以作为 a a a。如果 a [ i ] < max_k a[i] < \textit{max\_k} a[i]<max_k,那么它就可以作为 a a a,我们就找到了一组满足 a c b acb acb 模式的三元组;

    • 随后我们判断 a [ i ] a[i] a[i] 是否可以作为 c c c,以此找出哪些可以真正作为 b b b 的元素。我们将 a [ i ] a[i] a[i] 不断地与单调栈栈顶的元素进行比较,如果 a [ i ] a[i] a[i] 较大,那么栈顶元素可以真正作为 b b b,将其弹出并更新 max_k \textit{max\_k} max_k

    • 最后我们将 a [ i ] a[i] a[i] 作为 b b b 的候选元素放入单调栈中。这里可以进行一个优化,即如果 a [ i ] ≤ max_k a[i] \leq \textit{max\_k} a[i]max_k,那么我们也没有必要将 a [ i ] a[i] a[i] 放入栈中,因为即使它在未来被弹出,也不会将 max_k \textit{max\_k} max_k 更新为更大的值。

  • 在枚举完所有的元素后,如果仍未找到满足 acb 模式的三元组,那就说明其不存在。

C语言

#include<stdio.h>
#define bool int
#define false 0
#define true 1
bool find132pattern(int* nums, int numsSize) {
    int n = numsSize;
    int candidate_k[n], top = 0;
    candidate_k[top++] = nums[n - 1];
    int max_k = INT_MIN;

    for (int i = n - 2; i >= 0; --i) {
        if (nums[i] < max_k) {
            return true;
        }
        while (top && nums[i] > candidate_k[top - 1]) {
            max_k = candidate_k[--top];
        }
        if (nums[i] > max_k) {
            candidate_k[top++] = nums[i];
        }
    }

    return false;
}
int main()
{
	int n;
	scanf("%d",&n);
	int array[10000000];
	for(int i=0; i<n; ++i){
		scanf("%d",array+i);
	}
	bool ans = find132pattern(array,n);
	if(ans) printf("True");
	else printf("False");
	return 0;
}

C++

#include<vector>
#include<iostream>
class Solution {
public:
    bool find132pattern(vector<int>& nums) {
        int n = nums.size();
        stack<int> candidate_k;
        candidate_k.push(nums[n - 1]);
        int max_k = INT_MIN;

        for (int i = n - 2; i >= 0; --i) {
            if (nums[i] < max_k) {
                return true;
            }
            while (!candidate_k.empty() && nums[i] > candidate_k.top()) {
                max_k = candidate_k.top();
                candidate_k.pop();
            }
            if (nums[i] > max_k) {
                candidate_k.push(nums[i]);
            }
        }
        return false;
    }
};
int main()
{
    Solution sol;
    int n;
    cin >> n;
    vector<int> array(n);
    for (int i = 0; i < n; ++i){
        cin >> array[i];
    }
    bool res = sol.find132pattern(array);
	if(res){
        cout << "True";
	}
    else{
        cout << "False";
    }
    return 0;
}

Java

import java.io.*;
import java.util.*;
import java.util.TreeMap;
class Solution {
    public boolean find132pattern(int[] nums) {
        int n = nums.length;
        Deque<Integer> candidateK = new LinkedList<Integer>();
        candidateK.push(nums[n - 1]);
        int maxK = Integer.MIN_VALUE;

        for (int i = n - 2; i >= 0; --i) {
            if (nums[i] < maxK) {
                return true;
            }
            while (!candidateK.isEmpty() && nums[i] > candidateK.peek()) {
                maxK = candidateK.pop();
            }
            if (nums[i] > maxK) {
                candidateK.push(nums[i]);
            }
        }

        return false;
    }
}
public class Main {
    public static void main(String args[]) throws Exception {
        Scanner cin=new Scanner(System.in);
        int n = cin.nextInt();
        int[] arr = new int[n];
        for(int i =0;i<n;++i){
            arr[i]=cin.nextInt();
        }
        Solution sol = new Solution();
        if(sol.find132pattern(arr)){
            System.out.print("True");
        }else{
            System.out.print("False");
        }
    }
}

D题 方形星系

题目来源:spiral-matrix-ii

方法一:模拟

模拟矩阵的生成。按照要求,初始位置设为矩阵的左上角,初始方向设为向右。若下一步的位置超出矩阵边界,或者是之前访问过的位置,则顺时针旋转,进入下一个方向。如此反复直至填入 n 2 n^2 n2
个元素。

matrix \textit{matrix} matrix 为生成的矩阵,其初始元素设为 0 0 0。由于填入的元素均为正数,我们可以判断当前位置的元素值,若不为 0 0 0,则说明已经访问过此位置。

C语言

#include<stdio.h>
int** generateMatrix(int n, int* returnSize, int** returnColumnSizes) {
    int maxNum = n * n;
    int curNum = 1;
    int** matrix = malloc(sizeof(int*) * n);
    *returnSize = n;
    *returnColumnSizes = malloc(sizeof(int) * n);
    for (int i = 0; i < n; i++) {
        matrix[i] = malloc(sizeof(int) * n);
        memset(matrix[i], 0, sizeof(int) * n);
        (*returnColumnSizes)[i] = n;
    }
    int row = 0, column = 0;
    int directions[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};  // 右下左上
    int directionIndex = 0;
    while (curNum <= maxNum) {
        matrix[row][column] = curNum;
        curNum++;
        int nextRow = row + directions[directionIndex][0], nextColumn = column + directions[directionIndex][1];
        if (nextRow < 0 || nextRow >= n || nextColumn < 0 || nextColumn >= n || matrix[nextRow][nextColumn] != 0) {
            directionIndex = (directionIndex + 1) % 4;  // 顺时针旋转至下一个方向
        }
        row = row + directions[directionIndex][0];
        column = column + directions[directionIndex][1];
    }
    return matrix;
}
int main(){
	int n;
	scanf("%d",&n);
	int size,columnSizes;
	int** matrix = generateMatrix(m,&size,&columnSizes);
	for(int i=0;i<size;++i){
		for(int j=0;j<columnSizes;++j){
			if(j>0) printf(" ");
			printf("%d",matrix[i][j]);
		}
		printf("\n");
	}
	return 0;
}

C++

#include<vector>
#include<iostream>
using namespace std;
class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        int maxNum = n * n;
        int curNum = 1;
        vector<vector<int>> matrix(n, vector<int>(n));
        int row = 0, column = 0;
        vector<vector<int>> directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};  // 右下左上
        int directionIndex = 0;
        while (curNum <= maxNum) {
            matrix[row][column] = curNum;
            curNum++;
            int nextRow = row + directions[directionIndex][0], nextColumn = column + directions[directionIndex][1];
            if (nextRow < 0 || nextRow >= n || nextColumn < 0 || nextColumn >= n || matrix[nextRow][nextColumn] != 0) {
                directionIndex = (directionIndex + 1) % 4;  // 顺时针旋转至下一个方向
            }
            row = row + directions[directionIndex][0];
            column = column + directions[directionIndex][1];
        }
        return matrix;
    }
};
int main()
{
	int n;
	cin>>n;
	Solution sol;
	vector<vector<int>> matrix = sol.generateMatrix(n);
	for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
                if(j>0) cout<<" ";
                cout<<matrix[i][j];
            }
            cout<<endl;
    }
	return 0;
}

Java

import java.util.Scanner;
class Solution {
    public int[][] generateMatrix(int n) {
        int[][] matrix = new int[n][n];
        int index = 1;
        int row = 0, col = 0;
        final int[][] directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
        int directionIndex = 0;

        while (index <= n * n) {
            matrix[row][col] = index;
            int newRow = row + directions[directionIndex][0];
            int newCol = col + directions[directionIndex][1];
            if (newRow < 0 || newRow >= n || newCol < 0 || newCol >= n || matrix[newRow][newCol] != 0) {
                ++directionIndex;
                directionIndex %= 4;
            }
            row += directions[directionIndex][0];
            col += directions[directionIndex][1];
            ++index;
        }
        return matrix;
    }
}
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        Solution solution = new Solution();
        int n = in.nextInt();
        int[][] matrix = solution.generateMatrix(n);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if(j>0) System.out.print(" ");
                System.out.print(matrix[i][j]);
            }
            System.out.print("\n");
        }
    }
}

方法二:按层模拟

可以将矩阵看成若干层,首先填入矩阵最外层的元素,其次填入矩阵次外层的元素,直到填入矩阵最内层的元素。

定义矩阵的第 kk 层是到最近边界距离为 kk 的所有顶点。例如,下图矩阵最外层元素都是第 11 层,次外层元素都是第 22 层,最内层元素都是第 33 层。

[[1, 1, 1, 1, 1, 1],
[1, 2, 2, 2, 2, 1],
[1, 2, 3, 3, 2, 1],
[1, 2, 3, 3, 2, 1],
[1, 2, 2, 2, 2, 1],
[1, 1, 1, 1, 1, 1]]
对于每层,从左上方开始以顺时针的顺序填入所有元素。假设当前层的左上角位于 ( top , left ) (\textit{top}, \textit{left}) (top,left),右下角位于 ( bottom , right ) (\textit{bottom}, \textit{right}) (bottom,right),按照如下顺序填入当前层的元素。

从左到右填入上侧元素,依次为 ( top , left ) (\textit{top}, \textit{left}) (top,left) ( top , right ) (\textit{top}, \textit{right}) (top,right)

从上到下填入右侧元素,依次为 ( top + 1 , right ) (\textit{top} + 1, \textit{right}) (top+1,right) ( bottom , right ) (\textit{bottom}, \textit{right}) (bottom,right)

如果 left < right \textit{left} < \textit{right} left<right top < bottom \textit{top} < \textit{bottom} top<bottom,则从右到左填入下侧元素,依次为 ( bottom , right − 1 ) (\textit{bottom}, \textit{right} - 1) (bottom,right1) ( bottom , left + 1 ) (\textit{bottom}, \textit{left} + 1) (bottom,left+1),以及从下到上填入左侧元素,依次为 ( bottom , left ) (\textit{bottom}, \textit{left}) (bottom,left) ( top + 1 , left ) (\textit{top} + 1, \textit{left}) (top+1,left)

填完当前层的元素之后,将 left \textit{left} left top \textit{top} top 分别增加 1 1 1,将 right \textit{right} right bottom \textit{bottom} bottom 分别减少 1 1 1,进入下一层继续填入元素,直到填完所有元素为止。

C语言

#include<stdio.h>
int** generateMatrix(int n, int* returnSize, int** returnColumnSizes) {
    int num = 1;
    int** matrix = malloc(sizeof(int*) * n);
    *returnSize = n;
    *returnColumnSizes = malloc(sizeof(int) * n);
    for (int i = 0; i < n; i++) {
        matrix[i] = malloc(sizeof(int) * n);
        memset(matrix[i], 0, sizeof(int) * n);
        (*returnColumnSizes)[i] = n;
    }
    int left = 0, right = n - 1, top = 0, bottom = n - 1;
    while (left <= right && top <= bottom) {
        for (int column = left; column <= right; column++) {
            matrix[top][column] = num;
            num++;
        }
        for (int row = top + 1; row <= bottom; row++) {
            matrix[row][right] = num;
            num++;
        }
        if (left < right && top < bottom) {
            for (int column = right - 1; column > left; column--) {
                matrix[bottom][column] = num;
                num++;
            }
            for (int row = bottom; row > top; row--) {
                matrix[row][left] = num;
                num++;
            }
        }
        left++;
        right--;
        top++;
        bottom--;
    }
    return matrix;
}
int main(){
	int n;
	scanf("%d",&n);
	int size,columnSizes;
	int** matrix = generateMatrix(m,&size,&columnSizes);
	for(int i=0;i<size;++i){
		for(int j=0;j<columnSizes;++j){
			if(j>0) printf(" ");
			printf("%d",matrix[i][j]);
		}
		printf("\n");
	}
	return 0;
}

C++

#include<vector>
#include<iostream>
using namespace std;
class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        int num = 1;
        vector<vector<int>> matrix(n, vector<int>(n));
        int left = 0, right = n - 1, top = 0, bottom = n - 1;
        while (left <= right && top <= bottom) {
            for (int column = left; column <= right; column++) {
                matrix[top][column] = num;
                num++;
            }
            for (int row = top + 1; row <= bottom; row++) {
                matrix[row][right] = num;
                num++;
            }
            if (left < right && top < bottom) {
                for (int column = right - 1; column > left; column--) {
                    matrix[bottom][column] = num;
                    num++;
                }
                for (int row = bottom; row > top; row--) {
                    matrix[row][left] = num;
                    num++;
                }
            }
            left++;
            right--;
            top++;
            bottom--;
        }
        return matrix;
    }
};
int main()
{
	int n;
	cin>>n;
	Solution sol;
	vector<vector<int>> matrix = sol.generateMatrix(n);
	for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
                if(j>0) cout<<" ";
                cout<<matrix[i][j];
            }
            cout<<endl;
    }
	return 0;
}

Java

import java.util.Scanner;
class Solution {
    public int[][] generateMatrix(int n) {
        int num = 1;
        int[][] matrix = new int[n][n];
        int left = 0, right = n - 1, top = 0, bottom = n - 1;
        while (left <= right && top <= bottom) {
            for (int column = left; column <= right; column++) {
                matrix[top][column] = num;
                num++;
            }
            for (int row = top + 1; row <= bottom; row++) {
                matrix[row][right] = num;
                num++;
            }
            if (left < right && top < bottom) {
                for (int column = right - 1; column > left; column--) {
                    matrix[bottom][column] = num;
                    num++;
                }
                for (int row = bottom; row > top; row--) {
                    matrix[row][left] = num;
                    num++;
                }
            }
            left++;
            right--;
            top++;
            bottom--;
        }
        return matrix;
    }
}
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        Solution solution = new Solution();
        int n = in.nextInt();
        int[][] matrix = solution.generateMatrix(n);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if(j>0) System.out.print(" ");
                System.out.print(matrix[i][j]);
            }
            System.out.print("\n");
        }
    }
}

E题 乾坤挪移

方法一 假装移动数组

每次输出数组时,都从第 i i i 位输出,需要输出 n n n 个元素,可以利用取模运算符获得正确的下标

C语言

#include <stdio.h>
#include <malloc.h>
int main()
{
	int n;
	scanf("%d", &n);
	int* array = (int*)malloc(sizeof(int) * n); //动态创建一个n个元素的数组
	for (int i = 0; i < n; ++i)
	{
		scanf("%d", array + i);
	}
	for (int i = 0; i < n; ++i)
	{
		for (int j = i; j < i+n; ++j)
		{
			if (j > i) printf(" ");
			printf("%d", array[j % n]);
		}
		printf("\n");
	}
	free(array); //释放array
	array = NULL;
	return 0;
}

C++

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

int main()
{
	int n;
	cin >> n;
	vector<int> array(n);
	for (int i = 0; i < n; ++i)
	{
		cin >> array[i];
	}
	for (int i = 0; i < n; ++i)
	{
		for (int j = i; j < i+n; ++j)
		{
			if (j > i) cout << " ";
			cout << array[j % n];
		}
		cout << endl;
	}
	return 0;
}

Java

import java.util.Scanner;
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n;
        n = in.nextInt();
        int[] array = new int[n];
        for (int i = 0; i < n; i++) {
            array[i] = in.nextInt();
        }
        for (int i = 0; i < n; i++) {
            for (int j = i; j < i + n; j++) {
                if (j > i) System.out.print(" ");
                System.out.print(array[j % n]);
            }
            System.out.print("\n");
        }
    }
}

F题 下一个更小元素

题目来源:next-greater-element-ii

方法一:单调栈 + 循环数组

我们可以使用单调栈解决本题。单调栈中保存的是下标,从栈底到栈顶的下标在数组 nums \textit{nums} nums 中对应的值是单调不升的。

每次我们移动到数组中的一个新的位置 i i i,我们就将当前单调栈中所有对应值小于 nums [ i ] \textit{nums}[i] nums[i] 的下标弹出单调栈,这些值的下一个更大元素即为 nums [ i ] \textit{nums}[i] nums[i](证明很简单:如果有更靠前的更大元素,那么这些位置将被提前弹出栈)。随后我们将位置 i i i 入栈。

但是注意到只遍历一次序列是不够的,例如序列 [ 2 , 3 , 1 ] [2,3,1] [2,3,1],最后单调栈中将剩余 [ 3 , 1 ] [3,1] [3,1],其中元素 [ 1 ] [1] [1] 的下一个更大元素还是不知道的。

一个朴素的思想是,我们可以把这个循环数组「拉直」,即复制该序列的前 n − 1 n-1 n1 个元素拼接在原序列的后面。这样我们就可以将这个新序列当作普通序列,用上文的方法来处理。

而在本题中,我们不需要显性地将该循环数组「拉直」,而只需要在处理时对下标取模即可。

C语言

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int* nextGreaterElements(int* nums, int numsSize, int* returnSize) {
    *returnSize = numsSize;
    if (numsSize == 0) {
        return NULL;
    }
    int* ret = malloc(sizeof(int) * numsSize);
    memset(ret, -1, sizeof(int) * numsSize);

    int stk[numsSize * 2 - 1], top = 0;
    for (int i = 0; i < numsSize * 2 - 1; i++) {
        while (top > 0 && nums[stk[top - 1]] > nums[i % numsSize]) {
            ret[stk[top - 1]] = nums[i % numsSize];
            top--;
        }
        stk[top++] = i % numsSize;
    }
    return ret;
}
int main()
{
    int n;
    scanf("%d", &n);
    int* array = malloc(sizeof(int) * n);
    for (int i = 0; i < n; ++i)
    {
        scanf("%d", array+i);
    }
    int len;
    int* ans = nextGreaterElements(array, n, &len);

    for (int i = 0; i < n; ++i)
    {
        if (i > 0) printf(" ");
        printf("%d", ans[i]);
    }
    return 0;
}

C++

#include <iostream>
#include <vector>
#include <stack>
using namespace std;
class Solution {
public:
    vector<int> nextGreaterElements(vector<int>& nums) {
        int n = nums.size();
        vector<int> ret(n, -1);
        stack<int> stk;
        for (int i = 0; i < n * 2 - 1; i++) {
            while (!stk.empty() && nums[stk.top()] > nums[i % n]) {
                ret[stk.top()] = nums[i % n];
                stk.pop();
            }
            stk.push(i % n);
        }
        return ret;
    }
};
int main()
{
    int n;
    cin >> n;
    Solution sol;
    vector<int> array(n);
    for (int i = 0; i < n; ++i)
    {
        cin >> array[i];
    }
    auto ans = sol.nextGreaterElements(array);
    for (int i = 0; i < ans.size(); ++i)
    {
        if (i > 0) cout << " ";
        cout << ans[i];
    }

    return 0;
}

Java

import java.util.Arrays;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Scanner;

class Solution {
    public int[] nextGreaterElements(int[] nums) {
        int n = nums.length;
        int[] ret = new int[n];
        Arrays.fill(ret, -1);
        Deque<Integer> stack = new LinkedList<Integer>();
        for (int i = 0; i < n * 2 - 1; i++) {
            while (!stack.isEmpty() && nums[stack.peek()] > nums[i % n]) {
                ret[stack.pop()] = nums[i % n];
            }
            stack.push(i % n);
        }
        return ret;
    }
}
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int[] array = new int[n];
        for (int i = 0; i < n; i++) {
            array[i] = in.nextInt();
        }
        Solution sol = new Solution();
        int[] ans = sol.nextGreaterElements(array);
        for (int i = 0; i < n; i++) {
            if (i > 0) System.out.print(" ");
            System.out.print(array[i]);
        }
    }
}


G题 求解高次方程

方法一:二分查找

通过求导可得该函数在 [ 20 , 50 ] [20,50] [20,50] 上是单调递增函数,则设 f ( x 0 ) = 0 f(x_0)=0 f(x0)=0,有 x < x 0 , f ( x ) < f ( x 0 ) x<x_0, f(x)<f(x_0) x<x0,f(x)<f(x0) x > x 0 , f ( x ) > f ( x 0 ) x>x_0, f(x)>f(x_0) x>x0,f(x)>f(x0),函数图像如图
函数图像
可得在这一区间内,函数值是从小到大有序的,可以使用二分查找找到特定值

C

#include <stdio.h>
#include <math.h>

inline double f(const double x)
{
	// f(x)=x^4+70*x^3-4175*x^2+15750*x-275625;

	return pow(x, 4) + 70 * pow(x, 3) - 4175 * pow(x, 2) + 15750 * x - 275625;
}
double binarySearch(double left, double right)
{
	int i = 1;
	const double delta = 0.000000000001;
	double mid;
	while (left < right)
	{
		mid = (left + right) / 2;
		double val = f(mid);
		//cout <<i <<" "<< "f(" << mid << ")= " << val << endl;
		if (-delta <= val && val <= delta)
		{
			return mid;
		}
		else if (val < -delta)
		{
			left = mid + delta;
		}
		else
		{
			right = mid - delta;
		}
		++i;
	}
	return mid;
}

int main()
{
	double ans = binarySearch(20.0, 50.0);
	printf("%.6lf", ans);
	return 0;
}

C++

#include <iostream>
#include <math.h>
#include <iomanip>
using namespace std;
inline double f(double x){
	// f(x)=x^4+70*x^3-4175*x^2+15750*x-275625;
	return pow(x, 4) + 70 * pow(x, 3) - 4175 * pow(x, 2) + 15750 * x - 275625;
}
double binarySearch(double left, double right){
	int i = 1;
	double delta = 0.000000000001;
	double mid;
	while (left<right){
		mid = (left + right) / 2;
		double val = f(mid);
		//cout <<i <<" "<< "f(" << mid << ")= " << val << endl;
		if(-delta <= val && val <= delta){
			return mid;
		}
		else if(val<-delta){
			left = mid + delta;
		}
		else{
			right = mid - delta;
		}
		++i;
	}
	return mid;
}
int main(){
	double ans = binarySearch(20.0, 50.0);
	//格式控制 fixed 小数表示,setprecision(n)保留n位小数
	cout << setiosflags(ios::fixed) << setprecision(6) << ans;
	return 0;
}

H题 存在重复元素

题目来源:contains-duplicate

方法一:排序

在对数字从小到大排序之后,数组的重复元素一定出现在相邻位置中。因此,我们可以扫描已排序的数组,每次判断相邻的两个元素是否相等,如果相等则说明存在重复的元素。
但是时间复杂度为 n log ⁡ ( n ) n\log(n) nlog(n),会超时。

C

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#define scanf scanf_s
int cmp(const void* _a, const void* _b) {
    int a = *(int*)_a, b = *(int*)_b;
    return a - b;
}
bool containsDuplicate(int* nums, int numsSize) {
    qsort(nums, numsSize, sizeof(int), cmp);
    for (int i = 0; i < numsSize - 1; i++) {
        if (nums[i] == nums[i + 1]) {
            return true;
        }
    }
    return false;
}
int main()
{
    int n;
    scanf("%d", &n);
    int* array = malloc(sizeof(int) * n);
    for (int i = 0; i < n; ++i)
    {
        scanf("%d", array + i);
    }
    if (containsDuplicate(array, n))
        printf("true");
    else
        printf("false");
    free(array);
    return 0;
}

C++

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
    bool containsDuplicate(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        int n = nums.size();
        for (int i = 0; i < n - 1; i++) {
            if (nums[i] == nums[i + 1]) {
                return true;
            }
        }
        return false;
    }
};
int main()
{
	int n;
	cin >> n;
	vector<int> array(n);
	for (int i = 0; i < n; ++i)
	{
		cin >> array[i];
	}
    Solution sol;
    if (sol.containsDuplicate(array)) cout << "true";
    else cout << "false";
	return 0;
}

Java

import java.util.Arrays;
import java.util.Scanner;

class Solution {
    public boolean containsDuplicate(int[] nums) {
        Arrays.sort(nums);
        int n = nums.length;
        for (int i = 0; i < n - 1; i++) {
            if (nums[i] == nums[i + 1]) {
                return true;
            }
        }
        return false;
    }
}
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int[] array = new int[n];
        for (int i = 0; i < n; i++) {
            array[i] = in.nextInt();
        }
        Solution sol = new Solution();
        if(sol.containsDuplicate(array)) System.out.print("true");
        else System.out.print("false");

    }
}

方法二:哈希表

对于数组中每个元素,我们将它插入到哈希表中。如果插入一个元素时发现该元素已经存在于哈希表中,则说明存在重复的元素。

C++

#include<iostream>
#include<vector>
#include<set>
#include<unordered_set>
using namespace std;
class Solution {
public:
    bool containsDuplicate(vector<int>& nums) {
        //使用stl的set
        unordered_set<int> hash;
        for (auto& elem : nums){
            auto res = hash.insert(elem);
            if (!res.second) return true;
        }
        return false;
    }
};

int main() {
    int n;
    cin >> n;
    vector<int> vect(n);
    for (int i = 0; i < n; ++i) {
        scanf("%d", &vect[i]);
        //cin >> vect[i];
    }
    Solution sol;
    if (sol.containsDuplicate(vect)) {
        cout << "true";
    }
    else {
        cout << "false";
    }
    return 0;
}

Java

import java.util.*;
class Solution {
    public boolean containsDuplicate(int[] nums) {
        Set<Integer> set = new HashSet<Integer>();
        for (int x : nums) {
            if (!set.add(x)) {
                return true;
            }
        }
        return false;
    }
}
public class Main{
	public static void main(String[] args){
		Scanner in = new Scanner(System.in);
		int n = in.nextInt();
		int[] array = new int[n];
		for(int i=0;i<n;++i){
			array[i]=in.nextInt();
		}
		Solution sol = new Solution();
        if(sol.containsDuplicate(array)) System.out.print("true");
        else System.out.print("false");
	}
}
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值