【备战秋招】每日一题:2023.04.26-华为OD(第一题)-引擎模块初始化

news/2024/7/20 16:15:44 标签: 华为od, 算法, java, 华为, python

为了更好的阅读体检,可以查看我的算法学习网站
在线评测链接:P1229

题目内容

塔子哥是一名软件工程师,他正在为一家游戏公司开发一个新的3D引擎。这个引擎由多个模块组成,每个模块负责不同的功能,比如渲染、物理、音效、网络等。

为了提高性能和稳定性,塔子哥需要在游戏启动时初始化这些模块,但是有一个问题:不同的模块之间存在依赖关系,比如渲染模块依赖于物理模块,音效模块依赖于网络模块等。如果塔子哥不按照正确的顺序初始化这些模块,就会导致错误或崩溃。

为了解决以上问题,塔子哥决定开发一个代码分析工具,用来分析代码模块之间的依赖关系,用来确定模块的初始化顺序、是否有循环依赖等问题。

塔子哥的工具可以一次初始化一个或多个模块,只要它们之间没有依赖关系。他把这个过程称为引擎模块初始化。

现在,塔子哥已经得到了一组模块间的依赖关系,塔子哥需要计算需要引擎模块初始化的次数。

输入描述

输入第一行为一个整数 n n n ,表示模块总数。

接下来的 n n n 行依次表示模块 1 1 1 n n n 的依赖关系。

输入每行的第一个数为一个整数 m m m ,表示依赖的模块数量(不会超过 n n n ),之后的数字表示当前模块依赖的模块ID序列,该序列不会重复出现相同的数字,并且模块ID的取值一定在 [ 1 , n ] [1,n] [1,n] 之内。

每一行里面的数字按一个空格分隔。

1 ≤ m ≤ n ≤ 1000 1\le m\le n \le 1000 1mn1000

输出描述

输出”批量初始化次数”。

若有循环依赖无法完成初始化,则输出 − 1 -1 1

样例

样例一

输入

5
3 2 3 4
1 5
1 5
1 5
0

输出

3

样例解释

5 5 5 个模块。

模块 1 1 1 依赖模块 2 2 2 3 3 3 4 4 4

模块 2 2 2 依赖模块 5 5 5

模块 3 3 3 依款模块 5 5 5

模块 4 4 4 依赖模块 5 5 5

模块 5 5 5 没有依赖任何模块。

批量初始化顺序为 { 5 } → { 2 , 3 , 4 } → { 1 } \{5\}\rightarrow \{2,3,4\}\rightarrow \{1\} {5}{2,3,4}{1} ,共需”批量初始化” 3 3 3 次。

样例二

输入

3
1 2
1 3
1 1

输出

-1

样例解释

存在循环依赖,无法完成初始化,返回 − 1 -1 1

思路:拓扑排序

解法1:DAG上的最长路

根据题目意思以及样例,我们可以发现实际上就是给我们一张有向无环图。问DAG上的最长路径。我们可以使用拓扑排序+动态规划求解。

要求最长路径,我们定义 d p [ i ] dp[i] dp[i] 为从任意起点到点 i i i的最长路径长度,那么转移为:
d p i = m a x ( d p j ) + 1 , j ∈ p r e ( i ) dp_i = max(dp_j) + 1 , j \in pre(i) dpi=max(dpj)+1,jpre(i)
其中 p r e ( i ) pre(i) pre(i) i i i点的前驱节点。在拓扑排序的过程中进行刷表法即可。最终答案为 m a x ( d p ) max(dp) max(dp)

小知识

动态规划转移的编码实现时有两种常见方法,

刷表法:对每个点,用该点的值更新后继节点的值

填表法:对每个点,用所有前驱节点的值来更新该该的值

一般来说我们的转移方程都是以填表法的形式描述,但是在实现的过程中并不一定总是要采用填表法的形式。例如本题,由于拓扑排序访问节点的特点,我们自然考虑刷表法。当然本题也存在填表法的方法,即直接记忆化搜索求解DAG上的最长路。

关于这两种方法的更多讨论,见《算法竞赛入门经典》中动态规划一章。

解法2:拓扑排序

实际上解法一的 d p dp dp数组可以不要。我们直接拓扑排序,然后记录运行的轮数即可。这样就变成了拓扑排序裸题

类似题目推荐

这是一道比较基础的拓扑排序题。需要更多的习题来加强练习!

LeetCode

207. 课程表 - 级别1

Luogu-P1347 排序 级别2

放这道题是由于力扣上的这题:序列重建 收费了。。。 来个免费的版本做做

1857. 有向图中最大颜色值

拓扑排序 + 动态规划

*1494. 并行课程 II

很容易误以为是拓扑排序+贪心,但是实际上是一道竞赛难度的状压dp + 枚举子集的题。 可以不做

CodeFun2000

P1145 2023.4.2-网易有道翻译-研发岗-第四题-机器采草莓最优决策

P1029 华为秋招-2022.11.3-指令排序

代码

C++

解法1

#include<bits/stdc++.h>
using namespace std;
/*
	e:邻接表
	d:入度
	dp:从任意源点到该点的最长路径
*/
vector<int> e[10005];
int d[10005] , dp[10005];
int main(){
    // 读入 + 建图
    int n;
    cin >> n;
    for (int i = 1 ; i <= n; i ++){
        int m;
        cin >> m;
        for (int j = 1 ; j <= m ; j++){
            int x;
            cin >> x;
            e[x].push_back(i);
            d[i]++;
        }
    }
    // 拓扑排序
    queue<int> q;
    for (int i = 1 ; i <= n ; i++)
        if (d[i] == 0)
            q.push(i) , dp[i] = 1;
    while (q.size()){
        int u = q.front();
        q.pop();
        for (auto v : e[u]){
            // 转移
            dp[v] = max(dp[v] , dp[u] + 1);
            d[v]--;
            if (d[v] == 0){
                q.push(v);
            }
        }
    }
    // 输出答案
    int mx = 0;
    for (int i = 1 ; i <= n ; i++){
        if (dp[i] == 0){
            cout << -1 << endl;
            return 0;
        }
        mx = max(mx , dp[i]);
    }
    cout << mx << endl;
    return 0;
}

python_208">python

解法2

python">from collections import defaultdict,deque
n = int(input())
edges = defaultdict(list)
in_ = [0] *(n+1)

for i in range(1,n+1):
    temp = list(map(int,input().split()))
    for j in range(1,len(temp)):
        edges[temp[j]].append(i)
        in_[i] += 1

queue = deque()
for i in range(1, n+1):
    if in_[i] ==0:
        queue.append(i)

if not queue:
    print(-1, end=' ')
else:
    res = 0
    while queue:
        res += 1
        n = len(queue)
        for i in range(n):
            from_ = queue[0]
            queue.popleft()
            for to_ in edges[from_]:
                in_[to_] -= 1
                if in_[to_] == 0:
                    queue.append(to_)

    print(res)

Java

java">import java.util.*;

public class Main {
    public void solution(){
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int[] inDegree = new int[n + 1];
        inDegree[0] = -1;
        ArrayList<Integer>[] outDegree = new ArrayList[n + 1];
        for(int i = 1; i <= n; ++i){
            int m = scanner.nextInt();
            inDegree[i] += m;
            for(int j = 0; j < m; ++j){
                int from = scanner.nextInt();
                if(outDegree[from] == null)
                    outDegree[from] = new ArrayList<>();
                outDegree[from].add(i);
            }
        }
        scanner.close();
        int res = 0;
        boolean check;
        Queue<Integer> queue = new LinkedList<>();
        do {
            check = false;
            for(int i = 1; i < inDegree.length; ++i){
                if(inDegree[i] == 0){
                    inDegree[i] = -1;
                    check = true;
                    queue.offer(i);
                }
            }
            if(check)
                res += 1;
            while(!queue.isEmpty()){
                int from = queue.poll();
                if(outDegree[from] == null)
                    continue;
                for(int i = 0; i < outDegree[from].size(); ++i)
                    --inDegree[outDegree[from].get(i)];
            }
        }while(check);
        for(int i = 1; i < inDegree.length; ++i){
            if(inDegree[i] != -1){
                System.out.println(-1);
                return;
            }
        }
        System.out.println(res);
    }
    public static void main(String[] args) {
        Main m = new Main();
        m.solution();
    }
}

Go

package main

import (
	"bufio"
	"fmt"
	"os"
)

const N = 1e3 + 10

var n, m int

func main() {

	inDegrees := make([]int, N)
	outDegrees := make([][]int, N)
	for i := range outDegrees {
		outDegrees[i] = make([]int, 0)
	}

	in := bufio.NewReader(os.Stdin)
	out := bufio.NewWriter(os.Stdout)
	defer out.Flush()

	fmt.Fscan(in, &n)
	for i := 1; i <= n; i++ {
		fmt.Fscan(in, &m)
		var id int
		inDegrees[i] = m
		for j := 0; j < m; j++ {
			fmt.Fscan(in, &id)
			outDegrees[id] = append(outDegrees[id], i)
		}
	}

	queue := make([]int, 0)
	for i := 1; i <= n; i++ {
		if inDegrees[i] == 0 {
			queue = append(queue, i)
		}
	}
	if len(queue) == 0 {
		fmt.Fprintln(out, -1)
		return
	}

	res, count := 0, 0
	for len(queue) != 0 {
		size := len(queue)
		count += size
		for i := 0; i < size; i++ {
			front := queue[0]
			queue = queue[1:]
			for _, o := range outDegrees[front] {
				inDegrees[o]--
				if inDegrees[o] == 0 {
					queue = append(queue, o)
				}
			}
		}
		res++
	}
	if count != n {
		//循环依赖
		fmt.Fprintln(out, -1)
		return
	}
	fmt.Fprintln(out, res)
}

Js

/* JavaScript Node ACM模式 控制台输入获取 */
const readline = require("readline");
 
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});
 
const lines = [];
let n;
rl.on("line", (line) => {
  lines.push(line);
 
  if (lines.length == 1) {
    // 模块总数N
    n = lines[0] - 0;
  }
 
  if (n && lines.length == n + 1) {
    // 记录每个服务的入度值
    const inDegree = new Array(n + 1).fill(0);
    // 记录每个服务的所有后继服务
    const next = {};
 
    // 模块从1~N,这里将 i 作为模块标识
    for (let i = 1; i <= n; i++) {
      if (!next[i]) next[i] = [];
      const line = lines[i].split(" ").map(Number);
 
      // 统计入度值
      inDegree[i] = line[0];
 
      // 统计后继服务
      for (let j = 1; j < line.length; j++) {
        const pre = line[j];
        next[pre] ? next[pre].push(i) : (next[pre] = [i]);
      }
    }
 
    console.log(getResult(n, next, inDegree));
    lines.length = 0;
  }
});
 
/**
 *
 * @param {*} n 模块总数N
 * @param {*} next 记录每个服务的所有后继服务
 * @param {*} inDegree 记录每个服务的入度值
 * @returns 输出"批量初始化次数”。若有循环依赖无法完成初始化,则输出-1。
 */
function getResult(n, next, inDegree) {
  // queue记录入度值为0的点
  let queue = [];
  for (let i = 1; i <= n; i++) {
    if (inDegree[i] == 0) queue.push(i);
  }
 
  // ans记录"批量初始化次数”
  let ans = 0;
  // count记录已成功部署的模块,如果存在循环依赖,则最终count < n
  let count = 0;
 
  while (queue.length) {
    const newQueue = [];
 
    // 遍历入度值为0的模块
    for (let id of queue) {
      // 每遍历一个,就说明有一个模块部署成功
      count++;
      // 一个模块部署成功,则其后继模块的入度值需要减1
      for (let nextId of next[id]) {
        // 如果后继模块的入度值变为了0,则说明也可以部署了
        if (--inDegree[nextId] == 0) newQueue.push(nextId);
      }
    }
 
    // 这里使用newQueue的原因是,保证每次都能剥离一层入度为0的模块,这样会方便ans统计
    queue = newQueue;
    ans++;
  }
 
  // 如果存在循环依赖,则最后成功部署的模块数count < n
  return count == n ? ans : -1;
}
// by sunde

题目内容均收集自互联网,如如若此项内容侵犯了原著者的合法权益,可联系我: (CSDN网站注册用户名: 塔子哥学算法) 进行删除。


http://www.niftyadmin.cn/n/467946.html

相关文章

【虚拟机数据恢复】XenServer虚拟机磁盘数据被破坏的数据恢复案例

虚拟机数据恢复环境&#xff1a; 一台某品牌720服务器&#xff0c;4块STAT硬盘通过RAID卡组建raid10磁盘阵列。部署的XenServer虚拟化平台Windows Server操作系统&#xff0c;共两个虚拟磁盘&#xff1a;数据盘系统盘。服务器作为Web服务器使用&#xff0c;上层部署ASP SQL Se…

c++11中的多线程std::thread

c11中的多线程std::thread 在c11中提供了新的多线程的创建方式std::thread, 丰富了对于多线程的使用。 std::thread类的构造函数的格式如下所示&#xff1a; thread() noexcept; //default constructor 其中noexcept表示函数不会抛出异常&#xff0c;如果抛出异常程序…

[软件测试] 期末答辩问题

1. 给你一个网站&#xff0c;你该如何测试&#xff1f; &#xff08;1&#xff09;确定测试目标和范围&#xff0c;查找相关文档如需求说明书、网站设计文档等&#xff0c;分析测试需求&#xff08;2&#xff09;制定测试计划&#xff0c;确定测试范围和测试策略&#xff0c;一…

Hadoop --- HDFS配置与操作

hadoop的配置文件存放目录在 {HADOOP_HOME}/etc/hadoop 下&#xff0c; 与 hdfs相关的配置&#xff1a; core-site.xml、hdfs-site.xml core-site.xml&#xff1a; core-site 配置详解 新增属性信息&#xff1a; fs.defaultFS fs.defaultFS表示指定集群的文件系统类型是分布…

c4d+AI+PS设计广告展示架/销售柜台/展示盒子的建议

1、首先做出我标识出来的样子&#xff0c;这里称作A面。&#xff08;可用软件&#xff1a;PS、AI、cdr等&#xff09; 2、制作用于展示盒A面PNG图片&#xff08;PS来掏空空白处用于描边&#xff09;。 操作&#xff1a;按需求缩小图片&#xff0c;载入选区&#xff0c;新建图层…

vue 封装Axios详细代码说明

在Vue项目中使用Axios来进行网络请求是很常见的&#xff0c;如果我们每次都直接调用axios的API来进行请求的话&#xff0c;不仅代码重复度高&#xff0c;而且还会影响代码的可维护性。因此&#xff0c;我们可以通过封装一个Axios请求的工具类&#xff0c;来优化我们的代码。 以…

BCSP-玄子Java开发之Java Web编程CH07_使用JSP/Servlet开发复杂业务

BCSP-玄子Java开发之Java Web编程CH07_使用JSP/Servlet开发复杂业务 实现数据分页查 大量数据的展示 页面冗长、数据定位不方便&#xff0c;需要拖动页面才能浏览更多的数据一次性查询大量数据&#xff0c;数据库查询压力大&#xff0c;浪费系统资源 [外链图片转存失败,源站…

【软考网络管理员】2023年软考网管初级常见知识考点(11)-TCP和UDP详解

涉及知识点 传输控制协议TCP是什么&#xff0c;三次握手的概念理解&#xff0c;用户数据报协议UDP是什么&#xff0c;软考网络管理员常考知识点&#xff0c;软考网络管理员网络安全&#xff0c;网络管理员考点汇总。 原创于&#xff1a;CSDN博主-《拄杖盲学轻声码》&#xff0…