2 solutions
-
0
题目描述
你是一名网络工程师,正在开发一个虚拟机管理系统,需要管理虚拟机的资源ID。为了确保每个虚拟机的ID唯一且可复用,你设计了一个资源池,支持三种操作: 动态分配:从资源池中按顺序分配指定数量的资源ID,适用于新创建的虚拟机。 指定分配:指定一个资源ID进行分配,适用于恢复或迁移的虚拟机。 释放资源:释放已经分配的资源ID,使其可再次使用。 每当资源ID被分配或释放时,资源池的管理会调整顺序,使得资源ID能够合理复用。你需要在执行操作后输出资源池的第一个空闲资源ID。
思路
数组模拟双向链表
思路步骤
1.初始化资源池: 给定资源池的范围 [l, r],我们需要用两个数组分别表示每个资源ID的左邻居和右邻居,同时还需要一个数组表示某个资源ID是否被占用。
2.动态分配资源ID: 根据要求,动态分配会从资源池的开始位置,按顺序分配指定数量的ID。每次分配一个ID后,我们将其从可用资源链表中移除,并更新链表的头部指针。
3.指定分配资源ID: 如果指定分配的ID在资源池范围内且未被占用,则将其从可用链表中移除。如果该ID正好是资源池的头或尾,还需要特别处理。
4.释放资源ID: 释放的资源ID需要重新加入到资源池的尾部,同时恢复它与前后节点的连接。
5.操作执行后: 每次操作结束后,输出资源池中第一个空闲的资源ID。
引入链表后,带来新的问题:对于后两个操作,我们也需要同时删除/插入一个节点。单纯用链表是无法快速做到这一点的。
解决方法:直接使用数组来模拟链表,具体实现见代码。
注意:华为OJ只给了100ms。这意味着你需要在极低的常数下通过这题。所以只有C/C++可能过这题。而且不仅需要使用C/C++,也不允许使用STL中的List,而是需要手写链表 。 这直接导致了本场成为华为春招最难的一场,通过率极低,估计只有10%。
代码说明
1.初始化链表:创建两个数组 L 和 R,分别表示每个资源ID的左邻居和右邻居;另一个数组 ud 用于标记每个资源ID是否被使用。
2.动态分配:
检查是否有足够的可用资源; 按顺序从链表中取出 num 个资源ID; 移除这些ID,并更新链表的头部。
3.指定分配:
检查指定的资源ID是否在范围内且未被占用; 将其从链表中移除,更新其前后连接。
4.释放资源ID:
检查释放的资源ID是否在范围内且已经被占用; 将其加入链表的尾部,更新链表的连接关系。
5.输出结果:
输出当前链表的第一个空闲资源ID,即当前的链表头部。
代码
C++
AC
#include <iostream> using namespace std; const int N = 100010; int l, r; int n; // 每个节点的左节点和右节点 int L[N], R[N]; // 节点是否已经被使用 int ud[N]; // 剩余节点数量,当前起始节点,当前末尾节点 int remain, beg, ed; int main() { cin >> l >> r; // 建立“链表”关系 for(int i=l; i<=r; i++) { L[i] = i-1; R[i] = i+1; } cin >> n; beg = l, ed = r; remain = r-l+1; for(int i=0; i<n; i++) { int op, v; cin >> op >> v; if(op == 1) { // 不够分配 if(remain < v) continue; while(v--) { // “孤立”当前节点 R[L[beg]] = R[beg]; L[R[beg]] = L[beg]; ud[beg] = 1; remain--; // 更新起始节点 beg = R[beg]; } } else if(op == 2) { if(v < l || v > r || ud[v]) continue; // “孤立”当前节点 R[L[v]] = R[v]; L[R[v]] = L[v]; // 如果指定分配的节点是首尾节点,要特殊处理一下 if(v == beg) beg = R[v]; else if(v == ed) ed = L[v]; ud[v] = 1; remain--; } else { if(v < l || v > r || !ud[v]) continue; if(remain == 0) { // 当前资源全部用完,特殊处理 beg = ed = v; } else { // 把当前节点放到最后,建立前后关系 R[ed] = v; L[v] = ed; ed = v; } ud[v] = 0; remain++; } } cout << beg << endl; } // by JAY
python
超时
class Node: def __init__(self, r_id=None) -> None: self.pre = None self.next = None self.r_id = r_id dummy_head = Node() # 头部存储最近没使用过的结点 dummy_tail = Node() # 尾部存储最近刚使用过的结点 dummy_head.next = dummy_tail dummy_tail.pre = dummy_head id2node = {} def addTail(node:Node): global dummy_tail dummy_tail.pre.next = node node.pre = dummy_tail.pre node.next = dummy_tail dummy_tail.pre = node def removeNode(node:Node): node.pre.next = node.next node.next.pre = node.pre r1, r2 = [int(item) for item in input().split()] for r_id in range(r1, r2+1): node = Node(r_id) id2node[r_id] = node addTail(node) t = int(input()) for _ in range(t): op = [int(item) for item in input().split()] # 动态删除最前面的多个 if op[0]==1: delete_num = op[1] if delete_num>len(id2node): # 资源池中空闲资源不足时,动态分配失败,对资源池不进行任何操作。 continue node = dummy_head.next while node is not None and node.r_id is not None and delete_num>0: removeNode(node) del id2node[node.r_id] delete_num-=1 node = node.next # 删除指定的某个 elif op[0]==2: delete_id = op[1] if delete_id>=r1 and delete_id<=r2 and delete_id in id2node: node = id2node[delete_id] removeNode(node) del id2node[node.r_id] # 添加一个到尾部 elif op[0]==3: add_id = op[1] if add_id not in id2node: node = Node(add_id) addTail(node) id2node[add_id] = node print(dummy_head.next.r_id)
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
能够AC,出自233大佬之手!
package main import "io/ioutil" import "os" func read(buf []byte) (int, []byte) { for buf[0] < '0' || buf[0] > '9' { buf = buf[1:] } ans := 0 for len(buf) > 0 && buf[0] >= '0' && buf[0] <= '9' { ans = ans * 10 + int(buf[0]) - '0' buf = buf[1:] } return ans, buf } func write(out []byte, x int) []byte { cnt := 0 for x != 0 { out = append(out, byte(x % 10 + '0')) x /= 10 cnt++ } s := out[len(out) - cnt:] l, r := 0, cnt - 1 for l < r { s[l], s[r] = s[r], s[l] l++ r-- } return out } func endl(out []byte) []byte { return append(out, '\n') } const maxn int = 3000001 var nxt, pre, val, id [maxn]int func main() { buf, err := ioutil.ReadAll(os.Stdin) if err != nil { return } out := make([]byte, 0) l, buf := read(buf) r, buf := read(buf) t, buf := read(buf) head, curr, total := 0, 0, 0 mem := r - l + 1 for i := l; i <= r; i++ { total++ val[total] = i id[i] = total pre[total] = curr nxt[curr] = total curr = total } total++ tail := total nxt[total - 1] = tail pre[tail] = total - 1 for i := 1; i <= t; i++ { var op, num int op, buf = read(buf) num, buf = read(buf) if op == 1 { if mem >= num { mem -= num for i := nxt[head]; num > 0; num-- { id[val[i]] = 0 val[i] = 0 nxt[pre[i]] = nxt[i] pre[nxt[i]] = pre[i] nxtnode := nxt[i] nxt[i], pre[i] = -1, -1 i = nxtnode } } } else if op == 2 { if id[num] != 0 { node := id[num] pre[nxt[node]] = pre[node] nxt[pre[node]] = nxt[node] id[num] = 0 val[node] = 0 nxt[node], pre[node] = -1, -1 mem-- } } else { if id[num] == 0 && num >= l && num <= r { total++ id[num] = total val[total] = num pre[total] = pre[tail] nxt[pre[tail]] = total nxt[total] = tail pre[tail] = total mem++ } } } out = write(out, val[nxt[head]]) out = endl(out) os.Stdout.Write(out) }
Js
超时...
/* JavaScript Node ACM模式 控制台输入获取 */ const readline = require("readline"); const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); const lines = []; // 资源池范围 let start, end; // 操作个数 let n; rl.on("line", (line) => { lines.push(line); if (lines.length == 1) { [start, end] = lines[0].split(" ").map(Number); } if (lines.length == 2) { n = lines[1] - 0; } if (n && lines.length == n + 2) { // 资源池 const link = new Set(); for (let i = start; i <= end; i++) link.add(i); // 操作 const operates = lines.slice(2).map((line) => line.split(" ").map(Number)); console.log(getResult(start, end, link, operates)); lines.length = 0; } }); function getResult(start, end, link, operates) { // 操作类型,操作数 for (let [type, val] of operates) { switch (type) { case 1: // 如果操作类型是1,即动态分配,那么操作数含义就是:分配次数 // 【资源池中空闲资源ID不足时,动态分配失败,对资源池不进行任何操作】 // while (link.size > 0 && val > 0) { // const [num] = link; // link.delete(num); // val--; // } if (val >= link.size) link.clear(); else { link = new Set([...link].slice(val)); } break; case 2: // 如果操作类型是2,即指定分配,那么操作数含义就是:分配的资源ID // 【指定分配资源ID已经被占用或者不在资源池范围内时,对资源池不进行任何操作】 link.delete(val); break; case 3: // 如果操作类型是3,即释放,那么操作数含义就是:释放的资源ID // 【释放资源ID不在资源池范围内时或者已经是空闲资源ID时,对资源池不进行任何操作】 if (start <= val && val <= end) link.add(val); break; } } const [ans] = link; return ans; }
- 1
Information
- ID
- 14
- Time
- 100ms
- Memory
- 256MiB
- Difficulty
- 9
- Tags
- # Submissions
- 81
- Accepted
- 7
- Uploaded By