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

第一题: 甜甜花酿鸡

在线测评链接:http://121.196.235.151/p/P1082

题目描述

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

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

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

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

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

输入描述

输入三个整数 a , b , c a,b,c a,b,c , 用空格隔开。
1 ≤ a , b , c ≤ 1 0 9 1\le a,b,c\le 10^9 1a,b,c109

输出描述

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

样例

输入

3 3 3

输出

2

样例解释

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

题解:分类讨论

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

  • 情况1 a = b a=b a=b,那么显然,不利用 c c c的情况下,可以制作 ⌊ a 2 ⌋ \left \lfloor \frac{a}{2} \right \rfloor 2a个甜甜花酿鸡,如果利用 c c c的话,总共可以制作 ⌊ a + c 2 ⌋ \left \lfloor \frac{a+c}{2} \right \rfloor 2a+c个甜甜花酿鸡
  • 情况2 a ≠ b a\ne b a=b,可以利用 c c c尽可能去平衡 a a a b b b的值

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

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

本题可以直接假定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皇后

在线测评链接:http://121.196.235.151/p/P1083

题目描述

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

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

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

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

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

输入描述

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

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

1 ≤ n ≤ 1000 1\le n\le 1000 1n1000

输出描述

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

样例1

输入

3
. * .
. . .
. . .

输出

2

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

样例2

输入

3
. * .
. * .
. . .

输出

-1

题解:模拟

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

那道题的数据范围是 n ≤ 13 n\le 13 n13,因此是可以用DFS求解的,本题 n ≤ 1000 n\le 1000 n1000,必须要设计一个时间复杂度为 O ( n 2 ) O(n^2) O(n2)的算法才不会超时。

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

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

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

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)

第三题: 最长的通讯路径

在线测评链接:http://121.196.235.151/p/P1084

题目描述

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

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

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

输入描述

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

接下来的 n − 1 n-1 n1 行,每行输入两个正整数 u , v u,v u,v 和一个字符 c h r chr chr ,代表节点 u u u 和节点 v v v 有一条边连接。

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

1 ≤ n ≤ 1 0 5 1\le n \le 10^5 1n105

1 ≤ u , v ≤ n 1\le u,v\le n 1u,vn

保证输入的是一颗树。

输出描述

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

样例

输入

4
1 2 R
2 3 B
3 4 B

输出

2

样例解释

选择 1 − 2 − 3 1-2-3 123 的路径即可。

题解:树形DP

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

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

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

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

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

对于子节点 v v v而言,如果与父节点 i i i所连接的边为红色,则有 f [ i ] [ 0 ] = m a x ( f [ i ] [ 0 ] , f [ v ] [ 1 ] + 1 ) f[i][0]=max(f[i][0],f[v][1]+1) f[i][0]=max(f[i][0],f[v][1]+1)

如果与父节点 i i i所连接的边为蓝色,则有 f [ i ] [ 1 ] = m a x ( f [ i ] [ 1 ] , f [ v ] [ 0 ] + 1 ) f[i][1]=max(f[i][1],f[v][0]+1) f[i][1]=max(f[i][1],f[v][0]+1)

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

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

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(1, 0);  // 从根节点开始,自顶向下搜索
        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 = [[0, 0] for _ 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(1, 0)  # 从根节点开始,自顶向下搜索
print(res)
  • 9
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值