【笔试题汇总】科大讯飞笔试题汇总 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;
}
  • 23
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值