23年春招:暑期实习-米哈游-笔试真题卷(1)

第一题: 甜甜花酿鸡

在线测评链接:https://www.sspnote.com/oj/3/82

题目描述

薯条哥是一名厨艺达人,最近特别喜欢做甜甜花酿鸡,因为这种菜式的口感鲜美且具有一定的艺术性。

薯条哥的厨房里,堆满了新鲜的食材,其中包括大量的鸡肉和甜甜花。

然而,当他想要烹制一些甜甜花酿鸡时,他发现他的材料可能不够用。他有 个甜甜花, 个鸡肉,以及 个神秘的魔法食材,可以当作甜甜花或者鸡肉使用。

薯条哥知道做一只甜甜花酿鸡需要 个甜甜花和 个鸡肉。他希望尽可能多地制作甜甜花酿鸡,而不浪费任何食材。

现在他需要你的帮助,来计算他最多能制作多少只甜甜花酿鸡。

输入描述

输入三个整数 , 用空格隔开。

输出描述

一个整数,代表可以制作的甜甜花酿鸡的最大数量。

样例

输入

3 3 3

输出

2

样例解释

可以将两个万能食材当作一个甜甜花和一个鸡肉,所以是 4 4 1 可以制作两个甜甜花酿鸡。

题解:分类讨论

首先,由于 是魔法药材,我们先不考虑,我们分类讨论 的大小关系

  • 情况1 ,那么显然,不利用 的情况下,可以制作 个甜甜花酿鸡,如果利用 的话,总共可以制作 个甜甜花酿鸡
  • 情况2 ,可以利用 尽可能去平衡 的值

如果有 ,则可以先利用c使a和b尽可能相等,然后就把问题转换成情况1。

如果没有,则把 全部累加到较小的一方即可。

本题可以直接假定a<b,如果a>=b,交换a和b即可。

C++

#include <iostream>
using namespace std;
int main() {
    int a, b, c;
    cin >> a >> b >> c;
    // 保证 a<=b
    if (a > b) {
        swap(a, b);
    }
    // c可以先将a和b补至相等,然后再均匀分配
    if (c >= b - a) {
        c -= (b - a);  // 将a和b补至相等
        cout << (b + c / 2) / 2 << endl;  // 剩下的均匀分配即可
    } else {
        a += c;  // 将c累加至较小的一方
        cout << a / 2 << endl;
    }

    return 0;
}

Java

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int a = scanner.nextInt();
        int b = scanner.nextInt();
        int c = scanner.nextInt();
        // 保证 a<=b
        if (a > b) {
            int temp = a;
            a = b;
            b = temp;
        }
        // c可以先将a和b补至相等,然后再均匀分配
        if (c >= b - a) {
            c -= (b - a);  // 将a和b补至相等
            System.out.println((b + c / 2) / 2);  // 剩下的均匀分配即可
        } else {
            a += c;  // 将c累加至较小的一方
            System.out.println(a / 2);
        }
    }
}

Python

a,b,c=map(int,input().split())
if a>b:  #保证 a<=b
    a,b=b,a
if c>=b-a:  #c可以先将a和b补至相等,然后再均匀分配
    c-=(b-a)  #将a和b补至相等
    print((b+c//2)//2)  #剩下的均匀分配即可
else:
    a+=c  #将c累加至较小的一方
    print(a//2)

第二题: N皇后

在线测评链接:https://www.sspnote.com/oj/3/83

题目描述

薯条哥是一个热衷于国际象棋的棋手,他最近在研究 皇后问题。在国际象棋中,皇后是一种强大的棋子,能够沿着横、竖、斜线攻击其他棋子。

而在 皇后问题中,皇后也是一种强大的棋子,它能攻击同一行、同一列以及同一 度角斜线和 度角斜线上的其他皇后。

薯条哥手上拿着一个 的棋盘,上面已经放置了一些皇后。他希望再放置一个皇后,使得所有的皇后不会互相攻击。

对于一个 的棋盘,有多种不同的摆放皇后的方式,而有些摆法可能会导致皇后之间发生攻击,有些摆法则不会。

因此,薯条哥需要找到所有满足条件的摆法,以便让他更好地研究 皇后问题,你能帮薯条哥求出有多少种放置方案吗?

输入描述

第一行输入一个正整数 ,代表棋盘大小。

接下来的 行,每行输入一个仅由 . * 组成的字符串,其中 * 代表放置了一个皇后, .代表未放置皇后。

输出描述

输出薯条哥有多少种放置方案。如果给定的输入的棋盘中有两个皇后会互相攻击,则输出-1。

样例1

输入

3
. * .
. . .
. . .

输出

2

说明 只有左下角和右下角两个位置可以放置。

样例2

输入

3
. * .
. * .
. . .

输出

-1

题解:模拟

注意数据范围!这不是DFS的那道八皇后:https://www.luogu.com.cn/problem/P1219

那道题的数据范围是 ,因此是可以用DFS求解的,本题 ,必须要设计一个时间复杂度为 的算法才不会超时。

我们可以根据当前棋盘已经放置的皇后位置,来对棋盘的任意一个位置 是否可以放置皇后来做一个标记。

如果 点已经放置了皇后,那么第 行,第 列,以及 的两个对角线都不可放置皇后,对于行和列我们可以使用两个数组 来标记,对于两个对角线,我们可以使用 这两个标记数组来判断是否可以放置,举个例子,如下图所示,45度的斜线上的点,都满足 等于一个定值,因此可以用 标记数组来判断。同理可得135度斜线上的点,都满足 是一个定值,但是由于 可能小于0,因此最后还需要把数值+n,以保证映射在一个正整数区间。

alt

这样模拟一遍,最终我们再枚举每一个位置是否可以放置皇后,输出对应的合法数量即可。

C++

#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
char  g[N][N];
bool col[N] , row[N] , dig[N * 2] , idig[N * 2];  // 记录行,列,正负对角线是否有皇后
int main()
{
    int n;
    cin >> n;
    bool flag=true//检验棋盘上的皇后是否合法
    for (int i = 1 ; i <= n ; i++){
        for (int j = 1 ; j <= n ; j++){
            cin>>g[i][j];
            if (g[i][j] == '*'){
                if(row[i]||col[j]||dig[i-j+n]||idig[i+j]){
                    flag=false;
                }
                row[i]=col[j]=dig[i-j+n]=idig[i+j]=true;  //将行、列、两个对角都标记为true
            }
        }
    }
    if(!flag){
        puts("-1");
        return 0;
    }
    int cnt = 0;
    for (int i = 1 ; i <= n ; i++){
        for (int j = 1 ; j <= n ; j++){
            if (row[i] || col[j] || dig[i - j + n] || idig[i + j]) continue;
            cnt ++;
        }
    }
    cout << cnt << endl;
    return 0;
}

Java

import java.util.Scanner;

public class Main {
    static final int N = 1010;
    static char[][] g = new char[N][N];
    static boolean[] row = new boolean[N], col = new boolean[N], dig = new boolean[N * 2], idig = new boolean[N * 2];

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        boolean flag = true;

        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                g[i][j] = scanner.next().charAt(0);
                if (g[i][j] == '*') {
                    if (row[i] || col[j] || dig[i - j + n] || idig[i + j]) {
                        flag = false;
                    }
                    row[i] = col[j] = dig[i - j + n] = idig[i + j] = true;
                }
            }
        }

        if (!flag) {
            System.out.println("-1");
            return;
        }

        int cnt = 0;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                if (row[i] || col[j] || dig[i - j + n] || idig[i + j]) continue;
                cnt++;
            }
        }

        System.out.println(cnt);
    }
}

Python

N = 1010
g = [['' for _ in range(N)] for _ in range(N)]
row = [False] * N
col = [False] * N
dig = [False] * (N * 2)
idig = [False] * (N * 2)

n = int(input())
flag = True

for i in range(n):
    g[i]=input().split(' ')
    for j in range(n):
        if g[i][j] == '*':
            if row[i] or col[j] or dig[i - j + n] or idig[i + j]:
                flag = False
            row[i] = col[j] = dig[i - j + n] = idig[i + j] = True

if not flag:
    print("-1")
    exit(0)

cnt = 0
for i in range(n):
    for j in range(n):
        if row[i] or col[j] or dig[i - j + n] or idig[i + j]:
            continue
        cnt += 1

print(cnt)

第三题: 最长的通讯路径

在线测评链接:https://www.sspnote.com/oj/3/84

题目描述

薯条哥是一位研究者,他在研究网络传输时遇到了一个问题。

他拿到了一张通讯网络的拓扑结构图,其中每条通讯线路被染成了红色或者蓝色。

他想找到一条长度最长的通讯路径,使得路径上相邻的两条线路颜色不同。

输入描述

第一行输入一个正整数 , 代表节点数量。

接下来的 行,每行输入两个正整数 和一个字符 ,代表节点 和节点 有一条边连接。

若为 'R' 代表这条边是红色, 'B' 代表这条边是蓝色。

保证输入的是一颗树。

输出描述

一个正整数,代表薯条哥可以选择的路径最大长度。

样例

输入

4
1 2 R
2 3 B
3 4 B

输出

2

样例解释

选择 的路径即可。

题解:树形DP

默认以1为根节点进行DFS遍历。选用其他节点也是不影响的,因为会枚举以 为根节点的所有路径。

根据题目描述,整个路径必须要满足路径上相邻的两条边的颜色不同,因此,当我们枚举以 为根节点且与 所连接的边为红色/蓝色的最大长度来求解这道题。

首先,我们根据动态规划的三要素状态方程定义、状态转移方程、状态初始化来分析这道题

状态方程定义:定义 表示以 为根节点且与节点 所连接的边为红色/蓝色的最长路径。

状态转移方程:我们根据树形DP的核心思想:利用子节点的信息来更新父节点的信息可以得出

对于子节点 而言,如果与父节点 所连接的边为红色,则有

如果与父节点 所连接的边为蓝色,则有

对于以 为根节点的最长路径,显然是将 这两条路径连接起来,可以得到一条最长路径,如下图所示,图中1号节点的最长路径就是

状态初始化:所有状态初始化为0即可。

alt

C++

#include <bits/stdc++.h>
using namespace std;
const int N=1E5+10;
struct Node{
    int node,color;   //定义边的另一个节点、边的颜色 0表示R 1表示B
};
vector<Node>g[N];  //领接表的vector写法 仅适用于点权建图
int f[N][2];  //以i为根节点且与节点i所连边的颜色为红色/蓝色的最大长度
int res;
void dfs(int u,int fa) //如果是有向图 就不需要fa这个变量
{
    for(auto &x:g[u]) //访问u的所有节点
    {
        int son=x.node,color=x.color;
        if(son==fa)continue//无向边才需要这一句 保证每个节点只会被访问一次(不理解的可以直接背过)
        dfs(son,u);  //先遍历子节点,更新子节点信息,再由子节点来更新父节点信息
        f[u][color]=max(f[u][color],f[son][1-color]+1);  //根据状态方程,利用子节点信息更新父节点信息
        res=max(res,f[u][0]+f[u][1]);
    }
}

int main(){
    int n;
    cin>>n;
    unordered_map<char,int>mp={{'R',0},{'B',1}};  //颜色的哈希映射
    for(int i=1;i<n;i++){
        int a,b;
        char ch;
        cin>>a>>b>>ch;
        g[a].push_back({b,mp[ch]});  //a->b建立一条边
        g[b].push_back({a,mp[ch]});   //b->a建立一条边
    }
    dfs(1,0);  //从根节点开始,自顶向下搜索
    cout<<res<<endl;
    return 0;
}

Java

import java.util.*;

class Node {
    int node, color;  // 定义边的另一个节点、边的颜色 0表示R 1表示B
}

public class Main {
    static final int N = (int)1e5 + 10;
    static ArrayList<Node>[] g = new ArrayList[N];  // 邻接表的ArrayList写法,仅适用于点权建图
    static int[][] f = new int[N][2];  // 以i为根节点且与节点i所连边的颜色为红色/蓝色的最大长度
    static int res;

    static void dfs(int u, int fa) {  // 如果是有向图,就不需要fa这个变量
        for(Node x : g[u]) {  // 访问u的所有节点
            int son = x.node, color = x.color;
            if(son == fa) continue;  // 无向边才需要这一句,保证每个节点只会被访问一次
            dfs(son, u);  // 先遍历子节点,更新子节点信息,再由子节点来更新父节点信息
            f[u][color] = Math.max(f[u][color], f[son][1-color] + 1);  // 根据状态方程,利用子节点信息更新父节点信息
            res = Math.max(res, f[u][0] + f[u][1]);
        }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        Map<Character, Integer> mp = new HashMap<>();  // 颜色的哈希映射
        mp.put('R'0);
        mp.put('B'1);
        for(int i = 1; i <= n; i++) g[i] = new ArrayList<>();
        for(int i = 1; i < n; i++) {
            int a = sc.nextInt(), b = sc.nextInt();
            char ch = sc.next().charAt(0);
            g[a].add(new Node(){{
                node = b;
                color = mp.get(ch);
            }});  // a->b建立一条边
            g[b].add(new Node(){{
                node = a;
                color = mp.get(ch);
            }});  // b->a建立一条边
        }
        dfs(10);  // 从根节点开始,自顶向下搜索
        System.out.println(res);
    }
}

Python

import sys
sys.setrecursionlimit(10**6)  # 设置递归深度上限
class Node:
    def __init__(self, node, color):  # 定义边的另一个节点、边的颜色 0表示R 1表示B
        self.node = node
        self.color = color

N = int(1e5) + 10
g = [[] for _ in range(N)]  # 邻接表的列表写法,仅适用于点权建图
f = [[00for _ in range(N)]  # 以i为根节点且与节点i所连边的颜色为红色/蓝色的最大长度
res = 0

def dfs(u, fa):  # 如果是有向图,就不需要fa这个变量
    global res
    for x in g[u]:  # 访问u的所有节点
        son, color = x.node, x.color
        if son == fa: continue  # 无向边才需要这一句,保证每个节点只会被访问一次
        dfs(son, u)  # 先遍历子节点,更新子节点信息,再由子节点来更新父节点信息
        f[u][color] = max(f[u][color], f[son][1-color] + 1)  # 根据状态方程,利用子节点信息更新父节点信息
        res = max(res, f[u][0] + f[u][1])

n = int(input())
mp = {'R'0'B'1}  # 颜色的哈希映射
for i in range(1, n):
    a, b, ch = input().split()
    a, b = int(a), int(b)
    g[a].append(Node(b, mp[ch]))  # a->b建立一条边
    g[b].append(Node(a, mp[ch]))  # b->a建立一条边
dfs(10)  # 从根节点开始,自顶向下搜索
print(res)
  • 22
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值