【笔试题汇总】科大讯飞笔试题汇总 2024-04-21-科大讯飞春招笔试题

这里是KK爱Coding 团队的paoxiaomo,一个现役ACMer,之后将会持续更新算法笔记系列以及笔试题题解系列

感谢大家的订阅➕ 和 喜欢💗

01.硬币最少组合问题

问题描述

K小姐有一个爱好就是收集各种硬币。有一天,她在整理自己的硬币收藏时,突然想到了一个有趣的问题。如果给定一个总金额 amount,能够用手上现有的硬币面值组合成该金额,需要的硬币数量最少是多少枚呢?

现在已知K小姐有 5 种不同面值的硬币,分别为 1、2、5、10、20、50、100 元,每种面值的硬币数量都是无限的。请你帮助K小姐设计一个程序,快速计算出给定总金额所需的最少硬币组合。

输入格式

输入仅包含一个正整数 amount,表示要求组合的目标总金额,单位为元。

输出格式

输出最少硬币组合,从大到小按面值顺序输出,硬币面值间用空格分隔。若无法用现有面值组合出目标总金额,则输出 -1。

样例输入

8

样例输出

5 2 1

数据范围

1 \leq amount \leq 10^5

题解

这是一个典型的动态规划问题。可以定义一个一维数组 dp,其中 dp[i] 表示组合成金额 i 所需的最少硬币数量。

先将 dp 数组初始化为一个较大的值,表示暂时无法组合出该金额。然后对于每一种面值的硬币,遍历 amount,尝试更新 dp[j] 的值。具体来说,对于面值为 val 的硬币,可以将金额 j 拆分为 val 和 j-val 两部分,即 dp[j] = dp[j-val] + 1,表示组合成金额 j 的最少硬币数等于组合成金额 j-val 的最少硬币数再加上当前这枚面值为 val 的硬币。

最后,再从 dp[amount] 开始回溯,找出凑成总金额的硬币组合方案。具体做法是,从 amount 开始,如果 dp[j] = dp[last] - 1,即找到了上一个状态,就将两个状态的差值 last - j 加入答案数组中,然后令 last = j,直到 j 减小到 0 为止。因为要求输出的硬币面值要从大到小排列,所以最后再对答案数组逆序输出即可。

参考代码

  • Python

import sys

n = int(sys.stdin.readline().strip())
coins = [1, 2, 5, 10, 20, 50, 100]
INF = float('inf')

dp = [INF] * (n + 1)
dp[0] = 0

for val in coins:
    for j in range(val, n + 1):
        dp[j] = min(dp[j], dp[j - val] + 1)

if dp[n] == INF or n == 0:
    print(-1)
    exit(0)

result = []
last, j = n, n
while j >= 0:
    if dp[j] == dp[last] - 1:
        result.append(last - j)
        last = j
    j -= 1

print(*reversed(result))

  • Java

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] coins = {1, 2, 5, 10, 20, 50, 100};
        int INF = Integer.MAX_VALUE;

        int[] dp = new int[n + 1];
        Arrays.fill(dp, INF);
        dp[0] = 0;

        for (int val : coins) {
            for (int j = val; j <= n; j++) {
                dp[j] = Math.min(dp[j], dp[j - val] + 1);
            }
        }

        if (dp[n] == INF || n == 0) {
            System.out.println(-1);
            return;
        }

        List<Integer> result = new ArrayList<>();
        int last = n, j = n;
        while (j >= 0) {
            if (dp[j] == dp[last] - 1) {
                result.add(last - j);
                last = j;
            }
            j--;
        }

        Collections.reverse(result);
        for (int num : result) {
            System.out.print(num + " ");
        }
    }
}

  • Cpp

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

const int INF = 0x3f3f3f3f;

int main() {
    int n;
    cin >> n;
    vector<int> coins = {1, 2, 5, 10, 20, 50, 100};

    vector<int> dp(n + 1, INF);
    dp[0] = 0;

    for (int val : coins) {
        for (int j = val; j <= n; j++) {
            dp[j] = min(dp[j], dp[j - val] + 1);
        }
    }

    if (dp[n] == INF || n == 0) {
        cout << -1 << endl;
        return 0;
    }

    vector<int> result;
    int last = n, j = n;
    while (j >= 0) {
        if (dp[j] == dp[last] - 1) {
            result.push_back(last - j);
            last = j;
        }
        j--;
    }

    reverse(result.begin(), result.end());
    for (int num : result) {
        cout << num << " ";
    }

    return 0;
}

02.单词拆分问题

问题描述

LYA最近在学习一门新的外语,他发现有些外来词在词典中并没有收录。为了方便学习和记忆,他想将这些生词拆分成词典中已有的单词。

现在给定一个词典,其中每个单词都有一个正整数权重。对于一个待拆分的单词,可以将其拆分成词典中出现的单词,且拆分得到的单词序列的权重之和最大。

例如,词典中有 i、flytek、inly 三个单词,权重分别为 3、1、5。对于单词 ilytek,可以拆分成 i/flytek 或 ily/tek,对应的权重之和分别为 4 和 8,因此应该拆分为 ily/tek。

假设词典中最多有 100 个单词,每个单词的长度不超过 50 个字符。请你帮助LYA设计一个程序,能够根据词典将输入的单词拆分成权重之和最大的形式。

输入格式

第一行包含整数 N,表示词典中单词的个数。

接下来 N 行,每行包含一个单词和对应的权重值,以空格分隔。

最后一行输入一个待拆分的单词。

输出格式

如果可以将输入的单词拆分,则输出拆分后的结果,单词之间用 / 隔开。如果无法拆分,则原样输出待拆分的单词。

样例输入

7
ba 1
cef 2
cefs 3
s 2
dok 6
sdok 9
ok 3
bacefsdok

样例输出

/ba/cef/sdok

数据范围

1 \leq N \leq 100 1 \leq 单词长度 \leq 50

题解

本题可以使用动态规划来解决。设 dp[i] 表示将前 i 个字符拆分得到的最大权重和,fore[i] 表示前 i 个字符拆分得到最大权重和时,最后一个单词的前一个字符的下标。

我们可以从前往后枚举每个字符作为当前拆分的结束位置,然后枚举上一个单词的结束位置,判断中间这一段是否能在词典中找到。如果能找到,就更新 dp[i] 和 fore[i]。

最后,如果 dp[n] 的值没有被更新过,说明无法拆分,原样输出单词。否则,从 fore[n] 开始,依次向前跳转,就能得到拆分的单词序列。

第二种思路,使用前缀和来

参考代码

  • Python

def main():
    n = int(input())
    words = {}
    for _ in range(n):
        word, weight = input().split()
        words[word] = int(weight)

    s = input()
    m = len(s)
    dp = [-float('inf')] * (m + 1)
    fore = [-1] * (m + 1)
    dp[0] = 0

    for i in range(1, m + 1):
        for j in range(i):
            word = s[j:i]
            if word in words and dp[j] + words[word] > dp[i]:
                dp[i] = dp[j] + words[word]
                fore[i] = j

    if dp[m] == -float('inf'):
        print(s)
    else:
        res = []
        i = m
        while i > 0:
            res.append(s[fore[i]:i])
            i = fore[i]
        print('/' + '/'.join(res[::-1]))

if __name__ == "__main__":
    main()

  • Java

import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        Map<String, Integer> words = new HashMap<>();
        for (int i = 0; i < n; i++) {
            String[] input = br.readLine().split(" ");
            words.put(input[0], Integer.parseInt(input[1]));
        }

        String s = br.readLine();
        int m = s.length();
        int[] dp = new int[m + 1];
        Arrays.fill(dp, Integer.MIN_VALUE);
        int[] fore = new int[m + 1];
        dp[0] = 0;

        for (int i = 1; i <= m; i++) {
            for (int j = 0; j < i; j++) {
                String word = s.substring(j, i);
                if (words.containsKey(word) && dp[j] + words.get(word) > dp[i]) {
                    dp[i] = dp[j] + words.get(word);
                    fore[i] = j;
                }
            }
        }

        if (dp[m] == Integer.MIN_VALUE) {
            System.out.println(s);
        } else {
            List<String> res = new ArrayList<>();
            int i = m;
            while (i > 0) {
                res.add(s.substring(fore[i], i));
                i = fore[i];
            }
            Collections.reverse(res);
            System.out.println("/" + String.join("/", res));
        }
    }
}

  • Cpp

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

int main() {
    int n;
    cin >> n;
    unordered_map<string, int> words;
    for (int i = 0; i < n; i++) {
        string word;
        int weight;
        cin >> word >> weight;
        words[word] = weight;
    }

    string s;
    cin >> s;
    int m = s.length();
    vector<int> dp(m + 1, INT_MIN);
    vector<int> fore(m + 1);
    dp[0] = 0;

    for (int i = 1; i <= m; i++) {
        for (int j = 0; j < i; j++) {
            string word = s.substr(j, i - j);
            if (words.count(word) && dp[j] + words[word] > dp[i]) {
                dp[i] = dp[j] + words[word];
                fore[i] = j;
            }
        }
    }

    if (dp[m] == INT_MIN) {
        cout << s << endl;
    } else {
        vector<string> res;
        int i = m;
        while (i > 0) {
            res.push_back(s.substr(fore[i], i - fore[i]));
            i = fore[i];
        }
        reverse(res.begin(), res.end());
        cout << "/" << res[0];
        for (int i = 1; i < res.size(); i++) {
            cout << "/" << res[i];
        }
        cout << endl;
    }
    return 0;
}

03.K小姐的幸运评论

题目描述

K小姐是一位著名的博主,她在社交平台上发布了一篇文章。文章发布后,粉丝们纷纷在评论区留下了自己的观点和看法。为了鼓励大家积极评论,K小姐决定从评论者中选出一位"最幸运评论者",送出神秘大奖。

评论者的选取规则如下:

  1. 每个评论都有一个唯一的编号,从 0 开始递增。
  2. 每个评论下面都有点赞和点踩功能,定义每条评论的净赞数量为该评论点赞数减去点踩数。净赞数量可以是负数。
  3. 在编号连续的评论段中,存在一个净赞数量之和最大的区间,记为区间 [l,r]。
  4. 在区间 [l,r] 内净赞数最多的评论者即为"最幸运评论者"。如果有并列,则取编号最大的评论者。

现在请你帮助K小姐设计一个程序,处理评论数据,找出这位幸运的粉丝。

输入格式

第一行包含一个整数 n,表示评论的总数。

第二行包含 n 个整数,每个整数表示一条评论的净赞数量。

输出格式

输出共 1 行,包含 5 个整数 l,r,maxv,p,v,分别表示最大净赞区间的左右端点编号、区间内净赞总量、"最幸运评论者"的编号、该评论者的净赞数量。

样例输入

10
-99 -58 -52 7 89 48 43 -10 94 69

样例输出

3 9 340 8 94

数据范围

1 \leq n \leq 10^5 -10^4 \leq 净赞数量 \leq 10^4

题解

本题是一道经典的最大子段和问题,但是在此基础上还需要找出最大子段中的最大值及其位置。

我们可以使用动态规划来解决最大子段和问题。定义 dp[i] 表示以位置 i 结尾的最大子段和,dp[i] 的状态转移方程为:

dp[i]=\begin{cases}
nums[i] & ,\text{if } dp[i-1] \leq 0 \\
dp[i-1]+nums[i] &,\text{otherwise}
\end{cases}

同时我们需要记录区间的左右端点、区间内的最大值及其位置。

  • 当 dp[i-1] \leq 0 时,说明前面的子段和为负,舍弃前面的部分,从位置 i 开始重新计算区间。
  • 当 dp[i-1]>0 时,前面的子段和为正,可以继续延伸当前区间。
  • 记录最大子段和 maxSum,以及对应的左右端点 l,r。
  • 用单独的变量 maxNum,pos 记录区间内的最大值和对应下标。

最后,输出 l,r,maxSum,pos,maxNum 即可。

时间复杂度 O(n),空间复杂度 O(n)。其中 n 为数组长度。

参考代码

  • Python

n = int(input())
nums = list(map(int, input().split()))

l = r = 0
maxSum = float('-inf')
dp = [0] * n
dp[0] = nums[0]
maxNum = nums[0] 
pos = 0

for i in range(1, n):
    if dp[i-1] > 0:
        dp[i] = dp[i-1] + nums[i]
    else:
        dp[i] = nums[i]
        l = i

    if dp[i] > maxSum:
        maxSum = dp[i]
        r = i

    if nums[i] > maxNum:
        maxNum = nums[i]
        pos = i

print(l, r, maxSum, pos, maxNum)

  • Java

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] nums = new int[n];
        for (int i = 0; i < n; i++) {
            nums[i] = sc.nextInt();
        }

        int l = 0, r = 0;
        int maxSum = Integer.MIN_VALUE; 
        int[] dp = new int[n];
        dp[0] = nums[0];
        int maxNum = nums[0];
        int pos = 0;

        for (int i = 1; i < n; i++) {
            if (dp[i-1] > 0) {
                dp[i] = dp[i-1] + nums[i];
            } else {
                dp[i] = nums[i];
                l = i;
            }

            if (dp[i] > maxSum) {
                maxSum = dp[i];
                r = i;
            }

            if (nums[i] > maxNum) {
                maxNum = nums[i];
                pos = i;
            }
        }

        System.out.print(l + " " + r + " " + maxSum + " " + pos + " " + maxNum);
    }
}

  • Cpp

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

int main() {
    int n;
    cin >> n;
    vector<int> nums(n);
    for (int i = 0; i < n; i++) {
        cin >> nums[i];
    }

    int l = 0, r = 0;
    int maxSum = INT_MIN;
    vector<int> dp(n);
    dp[0] = nums[0];
    int maxNum = nums[0];
    int pos = 0;

    for (int i = 1; i < n; i++) {
        if (dp[i-1] > 0) {
            dp[i] = dp[i-1] + nums[i];
        } else {
            dp[i] = nums[i];
            l = i;
        }

        if (dp[i] > maxSum) {
            maxSum = dp[i];
            r = i;
        }

        if (nums[i] > maxNum) {
            maxNum = nums[i];
            pos = i;
        }
    }

    cout << l << " " << r << " " << maxSum << " " << pos << " " << maxNum << endl;

    return 0;
}
  • 24
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
STL STL库由哪部分组成 库由哪部分组成 库由哪部分组成 库由哪部分组成 :容器 、算法迭代容器 、算法迭代容器 、算法迭代容器 、算法迭代容器 、算法迭代。 简答题: 简答题: 1. 冒泡排序和快速的优缺 冒泡排序和快速的优缺 冒泡排序和快速的优缺 冒泡排序和快速的优缺 冒泡排序和快速的优缺 冒泡排序和快速的优缺 冒泡排序和快速的优缺 点 2. 进程和线共同使用的 技术(好像是这么说进程和线共同使用的 技术(好像是这么说进程和线共同使用的 技术(好像是这么说进程和线共同使用的 技术(好像是这么说进程和线共同使用的 技术(好像是这么说进程和线共同使用的 技术(好像是这么说进程和线共同使用的 技术(好像是这么说进程和线共同使用的 技术(好像是这么说进程和线共同使用的 技术(好像是这么说进程和线共同使用的 技术(好像是这么说) 3. 指针和引用的区 指针和引用的区 指针和引用的区 指针和引用的区 别 4. 析构函数和普通成员的区 析构函数和普通成员的区 析构函数和普通成员的区 析构函数和普通成员的区 析构函数和普通成员的区 析构函数和普通成员的区 析构函数和普通成员的区 别 1. 答案 : 冒泡排序 冒泡排序 :优点稳 :优点稳 :优点稳 定;缺点: ;缺点: 慢。 快速 排序:优点 排序:优点 排序:优点 快速 ;缺点:不稳定。 ;缺点:不稳定。 ;缺点:不稳定。 ;缺点:不稳定。 2. 3. 答案 引用访问 一个变量是直接,而指针间引用访问 一个变量是直接,而指针间引用访问 一个变量是直接,而指针间引用访问 一个变量是直接,而指针间引用访问 一个变量是直接,而指针间引用访问 一个变量是直接,而指针间引用访问 一个变量是直接,而指针间引用访问 一个变量是直接,而指针间引用访问 一个变量是直接,而指针间引用访问 一个变量是直接,而指针间。 引用是一个变量的别名,本身不单独分配自己内存空间 而指针有引用是一个变量的别名,本身不单独分配自己内存空间 而指针有引用是一个变量的别名,本身不单独分配自己内存空间 而指针有引用是一个变量的别名,本身不单独分配自己内存空间 而指针有引用是一个变量的别名,本身不单独分配自己内存空间 而指针有引用是一个变量的别名,本身不单独分配自己内存空间 而指针有引用是一个变量的别名,本身不单独分配自己内存空间 而指针有引用是一个变量的别名,本身不单独分配自己内存空间 而指针有引用是一个变量的别名,本身不单独分配自己内存空间 而指针有引用是一个变量的别名,本身不单独分配自己内存空间 而指针有引用是一个变量的别名,本身不单独分配自己内存空间 而指针有。 引用在开始的时候就绑定到了一个内存空间 引用在开始的时候就绑定到了一个内存空间 引用在开始的时候就绑定到了一个内存空间 引用在开始的时候就绑定到了一个内存空间 引用在开始的时候就绑定到了一个内存空间 引用在开始的时候就绑定到了一个内存空间 引用在开始的时候就绑定到了一个内存空间 引用在开始的时候就绑定到了一个内存空间 引用在开始的时候就绑定到了一个内存空间 引用在开始的时候就绑定到了一个内存空间 引用在开始的时候就绑定到了一个内存空间 引用在开始的时候就绑定到了一个内存空间 引用在开始的时候就绑定到了一个内存空间 引用在开始的时候就绑定到了一个内存空间 引用在开始的时候就绑定到了一个内存空间 引用在开始的时候就绑定到了一个内存空间 引用在开始的时候就绑定到了一个内存空间 引用在开始的时候就绑定到了一个内存空间 引用在开始的时候就绑定到了一个内存空间 (开始必须赋初值 开始必须赋初值 开始必须赋初值 开始必须赋初值 开始必须赋初值 开始必须赋初值 开始必须赋初值 ), 所以他只能是这 所以他只能是这 所以他只能是这 所以他只能是这 所以他只能是这 所以他只能是这 所以他只能是这 个内存 空间的名字 空间的名字 ,而不能改成其他的 而不能改成其他的 而不能改成其他的 而不能改成其他的 ,当然可以改变这个内存空间的 当然可以改变这个内存空间的 当然可以改变这个内存空间的 当然可以改变这个内存空间的 当然可以改变这个内存空间的 当然可以改变这个内存空间的 当然可以改变这个内存空间的 值。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值