校招算法笔面试 | 校招笔面试真题-小红的夹吃棋

题目## 题目## 题目## 题目

题目链接

题目链接

小红的夹吃棋

题目描述

在一个3*3的棋盘上,小红和小紫正在玩"夹吃棋"。所谓"夹吃棋",即如果存在一个白子,它的两侧(横向或者纵向)相邻都是黑子,则这个棋子将被"夹吃";对于黑棋亦然。如果一个棋盘的局面没有一方被夹吃,或者黑白双方都被对面夹吃,则认为是平局。如果只有一方夹吃了另一方,则认为夹吃方赢,被夹吃方输。

小红执黑棋,小紫执白棋,现在给定一个局面,请你判断当前棋局是谁获胜。

输入:

  • 第一行输入一个正整数,代表询问的次数
  • 接下来每组询问输入三行,每行是一个长度为3的字符串,字符串仅由’o’、‘*’、'.'组成
  • 其中’o’代表白棋,'*‘代表黑棋,’.'代表未放置棋子

输出:

  • 对于每个棋局,输出一行字符串表示答案
  • 小红获胜输出"kou",小紫获胜输出"yukari",平局输出"draw"

解题思路

这是一个模拟问题,可以通过以下步骤解决:

  1. 关键发现:

    • 需要检查每个棋子是否被夹吃
    • 只需要检查横向和纵向
    • 需要分别统计黑白双方被夹吃的情况
  2. 模拟策略:

    • 遍历棋盘上的每个位置
    • 对每个棋子检查横向和纵向是否被夹吃
    • 根据双方被夹吃情况判断胜负
  3. 具体步骤:

    • 读入棋盘状态
    • 检查每个白子是否被黑子夹吃
    • 检查每个黑子是否被白子夹吃
    • 根据规则判断胜负

代码

#include <bits/stdc++.h>
using namespace std;

// 检查位置(i,j)的棋子是否被夹吃
bool isEaten(vector<string>& board, int i, int j, char target, char enemy) {
    // 检查横向
    if(j > 0 && j < 2 && board[i][j] == target && 
       board[i][j-1] == enemy && board[i][j+1] == enemy) {
        return true;
    }
    // 检查纵向
    if(i > 0 && i < 2 && board[i][j] == target && 
       board[i-1][j] == enemy && board[i+1][j] == enemy) {
        return true;
    }
    return false;
}

string solve(vector<string>& board) {
    bool white_eaten = false;  // 白子是否被夹吃
    bool black_eaten = false;  // 黑子是否被夹吃
    
    // 检查每个位置
    for(int i = 0; i < 3; i++) {
        for(int j = 0; j < 3; j++) {
            if(board[i][j] == 'o' && isEaten(board, i, j, 'o', '*')) {
                white_eaten = true;
            }
            if(board[i][j] == '*' && isEaten(board, i, j, '*', 'o')) {
                black_eaten = true;
            }
        }
    }
    
    // 判断胜负
    if(white_eaten && !black_eaten) return "kou";      // 小红赢
    if(!white_eaten && black_eaten) return "yukari";   // 小紫赢
    return "draw";                                     // 平局
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int T;
    cin >> T;
    while(T--) {
        vector<string> board(3);
        for(int i = 0; i < 3; i++) {
            cin >> board[i];
        }
        cout << solve(board) << '\n';
    }
    return 0;
}
import java.util.*;

public class Main {
    // 检查位置(i,j)的棋子是否被夹吃
    static boolean isEaten(char[][] board, int i, int j, char target, char enemy) {
        // 检查横向
        if(j > 0 && j < 2 && board[i][j] == target && 
           board[i][j-1] == enemy && board[i][j+1] == enemy) {
            return true;
        }
        // 检查纵向
        if(i > 0 && i < 2 && board[i][j] == target && 
           board[i-1][j] == enemy && board[i+1][j] == enemy) {
            return true;
        }
        return false;
    }
    
    static String solve(char[][] board) {
        boolean whiteEaten = false;  // 白子是否被夹吃
        boolean blackEaten = false;  // 黑子是否被夹吃
        
        // 检查每个位置
        for(int i = 0; i < 3; i++) {
            for(int j = 0; j < 3; j++) {
                if(board[i][j] == 'o' && isEaten(board, i, j, 'o', '*')) {
                    whiteEaten = true;
                }
                if(board[i][j] == '*' && isEaten(board, i, j, '*', 'o')) {
                    blackEaten = true;
                }
            }
        }
        
        // 判断胜负
        if(whiteEaten && !blackEaten) return "kou";      // 小红赢
        if(!whiteEaten && blackEaten) return "yukari";   // 小紫赢
        return "draw";                                   // 平局
    }
    
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int T = sc.nextInt();
        while(T-- > 0) {
            char[][] board = new char[3][3];
            for(int i = 0; i < 3; i++) {
                board[i] = sc.next().toCharArray();
            }
            System.out.println(solve(board));
        }
    }
}
def is_eaten(board, i, j, target, enemy):
    # 检查横向
    if j > 0 and j < 2 and board[i][j] == target and \
       board[i][j-1] == enemy and board[i][j+1] == enemy:
        return True
    # 检查纵向
    if i > 0 and i < 2 and board[i][j] == target and \
       board[i-1][j] == enemy and board[i+1][j] == enemy:
        return True
    return False

def solve(board):
    white_eaten = False  # 白子是否被夹吃
    black_eaten = False  # 黑子是否被夹吃
    
    # 检查每个位置
    for i in range(3):
        for j in range(3):
            if board[i][j] == 'o' and is_eaten(board, i, j, 'o', '*'):
                white_eaten = True
            if board[i][j] == '*' and is_eaten(board, i, j, '*', 'o'):
                black_eaten = True
    
    # 判断胜负
    if white_eaten and not black_eaten:
        return "kou"       # 小红赢
    if not white_eaten and black_eaten:
        return "yukari"    # 小紫赢
    return "draw"          # 平局

T = int(input())
for _ in range(T):
    board = [input() for _ in range(3)]
    print(solve(board))

算法及复杂度

  • 算法:模拟
  • 时间复杂度: O ( T ) \mathcal{O}(T) O(T) - 每个棋局只需要常数时间检查
  • 空间复杂度: O ( 1 ) \mathcal{O}(1) O(1) - 只需要常数空间存储棋盘

题目链接

题目链接

小红的多彩糖葫芦

题目描述

小红喜欢吃多彩糖葫芦,多彩糖葫芦上的每一个糖葫芦都有一种颜色。但小红有非常严重的强迫症,她绝对不会连续吃两个相同颜色的糖葫芦。一串糖葫芦只能从上往下吃,一旦小红发现下一颗糖葫芦和她刚吃过的糖葫芦颜色相同时,小红就会把整串多彩糖葫芦丢掉。小红想知道她吃一串多彩糖葫芦时可以吃到几颗糖葫芦。

输入:

  • 一个字符串,表示多彩糖葫芦串上从上到下每一颗糖葫芦的颜色

输出:

  • 一个整数,表示小红最多能吃到的糖葫芦数量

解题思路

这是一个模拟问题,可以通过以下步骤解决:

  1. 关键发现:

    • 小红只能从上往下吃
    • 遇到相同颜色就会停止
    • 需要记录上一个吃的糖葫芦的颜色
  2. 模拟策略:

    • 从第一个糖葫芦开始吃
    • 记录上一个吃的糖葫芦颜色
    • 如果下一个糖葫芦颜色相同,就停止
    • 否则继续吃并更新上一个颜色
  3. 具体步骤:

    • 遍历字符串
    • 维护上一个颜色和已吃数量
    • 遇到相同颜色就退出循环

代码

#include <bits/stdc++.h>
using namespace std;

int main() {
    string s;
    cin >> s;
    
    int ans = 1;  // 第一个糖葫芦一定能吃
    char last = s[0];  // 记录上一个颜色
    
    for(int i = 1; i < s.length(); i++) {
        if(s[i] == last) {  // 遇到相同颜色就停止
            break;
        }
        last = s[i];  // 更新上一个颜色
        ans++;  // 可以吃这个糖葫芦
    }
    
    cout << ans << endl;
    return 0;
}
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String s = sc.next();
        
        int ans = 1;  // 第一个糖葫芦一定能吃
        char last = s.charAt(0);  // 记录上一个颜色
        
        for(int i = 1; i < s.length(); i++) {
            if(s.charAt(i) == last) {  // 遇到相同颜色就停止
                break;
            }
            last = s.charAt(i);  // 更新上一个颜色
            ans++;  // 可以吃这个糖葫芦
        }
        
        System.out.println(ans);
    }
}
s = input()

ans = 1  # 第一个糖葫芦一定能吃
last = s[0]  # 记录上一个颜色

for i in range(1, len(s)):
    if s[i] == last:  # 遇到相同颜色就停止
        break
    last = s[i]  # 更新上一个颜色
    ans += 1  # 可以吃这个糖葫芦

print(ans)

算法及复杂度

  • 算法:模拟
  • 时间复杂度: O ( n ) \mathcal{O}(n) O(n) - 最多需要遍历整个字符串
  • 空间复杂度: O ( 1 ) \mathcal{O}(1) O(1) - 只需要常数空间存储变量

题目链接

题目链接

小红的地砖

题目描述

小红有 n n n 块地砖,小红从第一块地砖开始,要走到第 n n n 块地砖。走到第 i i i 块地砖需要消耗 a i a_i ai 的体力值,小红每次可以选择向前走一步或者向前走两步,求小红走到第 n n n 块地砖时消耗的最小体力值。

输入:

  • 第一行输入一个整数 n n n,表示地砖的数量
  • 第二行输入 n n n 个整数 a i a_i ai,表示走到第 i i i 块地砖需要消耗的体力值

输出:

  • 输出一个整数,表示小红走到第 n n n 块地砖时消耗的最小体力值

解题思路

这是一个动态规划问题,可以通过以下步骤解决:

  1. 关键发现:

    • 每次可以走一步或两步
    • 到达每个位置的最小体力值只与前两个位置有关
    • 需要累加经过的地砖的体力值
  2. 解题策略:

    • 使用dp数组记录到达每个位置的最小体力值
    • 对每个位置,考虑从前一个位置走一步或从前两个位置走两步
    • 选择较小的体力值
  3. 具体步骤:

    • 初始化dp数组,dp[i]表示到达第i个位置的最小体力值
    • 状态转移:dp[i] = min(dp[i-1], dp[i-2]) + a[i]
    • 最后返回dp[n-1]

代码

#include <bits/stdc++.h>
using namespace std;

int main() {
    int n;
    cin >> n;
    vector<int> a(n);
    for(int i = 0; i < n; i++) {
        cin >> a[i];
    }
    
    vector<int> dp(n, INT_MAX);
    dp[0] = a[0];
    if(n > 1) dp[1] = a[1];
    
    for(int i = 2; i < n; i++) {
        dp[i] = min(dp[i-1], dp[i-2]) + a[i];
    }
    
    cout << dp[n-1] << endl;
    return 0;
}
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] a = new int[n];
        for(int i = 0; i < n; i++) {
            a[i] = sc.nextInt();
        }
        
        int[] dp = new int[n];
        Arrays.fill(dp, Integer.MAX_VALUE);
        dp[0] = a[0];
        if(n > 1) dp[1] = a[1];
        
        for(int i = 2; i < n; i++) {
            dp[i] = Math.min(dp[i-1], dp[i-2]) + a[i];
        }
        
        System.out.println(dp[n-1]);
    }
}
n = int(input())
a = list(map(int, input().split()))

dp = [float('inf')] * n
dp[0] = a[0]
if n > 1:
    dp[1] = a[1]

for i in range(2, n):
    dp[i] = min(dp[i-1], dp[i-2]) + a[i]

print(dp[n-1])

算法及复杂度

  • 算法:动态规划
  • 时间复杂度: O ( n ) \mathcal{O}(n) O(n) - 需要遍历一次数组
  • 空间复杂度: O ( n ) \mathcal{O}(n) O(n) - 需要dp数组存储状态

注意:

  1. 需要特别处理n=1和n=2的情况
  2. 初始化dp数组时要用最大值
  3. 状态转移时需要考虑前两个位置
  4. 最终结果是dp[n-1]而不是dp[n]

题目链接

题目链接

小红的回文子串

题目描述

小红拿到了一个字符串。她想知道,该字符串有多少个长度为 k 的连续子串是回文的?

输入:

  • 第一行输入两个正整数 n 和 k,分别代表字符串长度和子串长度
  • 第二行输入一个长度为 n 的、仅由小写字母组成的字符串

输出:

  • 输出一个整数,表示长度为 k 的回文子串数量

解题思路

这是一个字符串处理问题,可以通过以下步骤解决:

  1. 关键发现:

    • 需要检查所有长度为 k 的连续子串
    • 子串总数为 n-k+1 个
    • 每个子串需要判断是否为回文串
  2. 解题策略:

    • 使用滑动窗口遍历所有长度为 k 的子串
    • 对每个子串判断是否为回文
    • 统计回文子串的数量
  3. 具体步骤:

    • 遍历字符串的每个位置 i (0 到 n-k)
    • 检查从位置 i 开始的长度为 k 的子串
    • 判断该子串是否为回文
    • 累计回文子串的数量

代码

#include <bits/stdc++.h>
using namespace std;

// 判断字符串是否为回文
bool isPalindrome(const string& s, int start, int len) {
    int left = start;
    int right = start + len - 1;
    while(left < right) {
        if(s[left] != s[right]) return false;
        left++;
        right--;
    }
    return true;
}

int main() {
    int n, k;
    cin >> n >> k;
    string s;
    cin >> s;
    
    int ans = 0;
    // 遍历所有长度为k的子串
    for(int i = 0; i <= n - k; i++) {
        if(isPalindrome(s, i, k)) {
            ans++;
        }
    }
    
    cout << ans << endl;
    return 0;
}
import java.util.*;

public class Main {
    // 判断字符串是否为回文
    static boolean isPalindrome(String s, int start, int len) {
        int left = start;
        int right = start + len - 1;
        while(left < right) {
            if(s.charAt(left) != s.charAt(right)) return false;
            left++;
            right--;
        }
        return true;
    }
    
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int k = sc.nextInt();
        String s = sc.next();
        
        int ans = 0;
        // 遍历所有长度为k的子串
        for(int i = 0; i <= n - k; i++) {
            if(isPalindrome(s, i, k)) {
                ans++;
            }
        }
        
        System.out.println(ans);
    }
}
def is_palindrome(s, start, length):
    left = start
    right = start + length - 1
    while left < right:
        if s[left] != s[right]:
            return False
        left += 1
        right -= 1
    return True

n, k = map(int, input().split())
s = input()

ans = 0
# 遍历所有长度为k的子串
for i in range(n - k + 1):
    if is_palindrome(s, i, k):
        ans += 1

print(ans)

算法及复杂度

  • 算法:滑动窗口 + 回文判断
  • 时间复杂度: O ( n ⋅ k ) \mathcal{O}(n \cdot k) O(nk) - 需要检查 n − k + 1 n-k+1 nk+1 个子串,每个子串需要 O ( k ) \mathcal{O}(k) O(k) 时间判断
  • 空间复杂度: O ( 1 ) \mathcal{O}(1) O(1) - 只需要常数空间存储变量
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值