写点什么

2023-01-02:某天,小美在玩一款游戏,游戏开始时,有 n 台机器, 每台机器都有一个能量水平,分别为 a1、a2、…、an, 小美每次操作可以选其中的一台机器,假设选的是第 i 台, 那小美可以将其变成

  • 2023-01-02
    北京
  • 本文字数:3603 字

    阅读完需:约 12 分钟

2023-01-02:某天,小美在玩一款游戏,游戏开始时,有n台机器, 每台机器都有一个能量水平,分别为a1、a2、…、an, 小美每次操作可以选其中的一台机器,假设选的是第i台, 那小美可以将其变成

2023-01-02:某天,小美在玩一款游戏,游戏开始时,有 n 台机器,每台机器都有一个能量水平,分别为 a1、a2、…、an,小美每次操作可以选其中的一台机器,假设选的是第 i 台,那小美可以将其变成 ai+10^k(k 为正整数且 0<=k<=9),由于能量过高会有安全隐患,所以机器会在小美每次操作后会自动释放过高的能量即变成 (ai+10^k)%m 其中 %m 表示对 m 取模,由于小美还有工作没有完成,所以她想请你帮她计算一下,对于每台机器,将其调节至能量水平为 0 至少需要多少次操作(机器自动释放能量不计入小美的操作次数)。第一行两个正整数 n 和 m,表示数字个数和取模数值。第二行为 n 个正整数 a1, a2,...... an,其中 ai 表示第 i 台机器初始的能量水平。1 <= n <= 30000,2 <= m <= 30000, 0 <= ai <= 10^12。来自美团。


答案 2023-01-02:


打表法。用 rust 和 solidity 写代码。


代码用 rust 编写。代码如下:


use std::iter::repeat;fn main() {    let n = 5;    let m = 11;    let mut arr = vec![1, 3, 5, 7, 9];    let ans = times(n, m, &mut arr);    println!("ans = {:?}", ans);}
fn times(n: i32, m: i32, arr: &mut Vec<i32>) -> Vec<i32> { // map[i] : i这个余数变成余数0,需要至少操作几次? let mut map: Vec<i32> = repeat(0).take(m as usize).collect(); bfs(m, &mut map); let mut ans: Vec<i32> = repeat(0).take(n as usize).collect(); for i in 0..n { let num = arr[i as usize]; let mut min_times = i32::MAX; if num < m { min_times = map[num as usize]; } else { let mut add: i64 = 1; while add <= 1000000000 { let mod0: i32 = ((num as i64 + add) % m as i64) as i32; min_times = get_min(min_times, map[mod0 as usize] + 1); add *= 10; } } ans[i as usize] = min_times; } return ans;}
fn bfs(m: i32, map: &mut Vec<i32>) { let mut visited: Vec<bool> = repeat(false).take(m as usize).collect(); visited[0] = true; let mut queue: Vec<i32> = repeat(0).take(m as usize).collect(); let mut l = 0; let mut r = 1; // map[0] == 0 // 表示余数0变成余数0,需要至少0次 // 0进队列了, queue[0] = 0 // 0算访问过了,visited[0] = true while l < r { // 当前弹出的余数是cur let cur = queue[l as usize]; l += 1; // 能加的数字,从1枚举到10^9 let mut add: i64 = 1; while add <= 1000000000 { // 比如,m == 7 // 当前余数是cur,cur变成余数0,至少要a次 // 我们想知道 : (哪个余数b + add) % m == cur // 比如,add=10的时候,cur==5的时候 // 我们想知道 : (哪个余数b + 10) % 7 == 5 // 因为10 % 7 = 3 // 所以其实我们在求 : 哪个余数b + 3 == 5 // 显然b = 5 - 3 = cur - (add % m) = 2 // 再比如,add=10的时候,cur==2的时候 // 我们想知道 : (哪个余数b + 10) % 7 == 2 // 因为10 % 7 = 3 // 所以其实我们在求 : 哪个余数b + 3 == 2 // 这明显是不对的, // 所以其实我们在求 : 哪个余数b + 3 == 2 + m == 9 // 也就是b,通过加了add % m,来到了m + cur,多转了一圈 // b = 9 - 3 = cur - (add % m) + m = 6 // 也就是说,b = cur - (add % m), // 如果不小于0,那就是这个b,是我们要找的余数 // 如果小于0,那就是b+m,是我们要找的余数 let mut from: i32 = cur - (add % m as i64) as i32; if from < 0 { from += m; } // 这个余数我们终于找到了,因为cur变成余数0,需要a次 // 所以这个余数变成余数0,需要a+1次 // 当然前提是这个余数,之前宽度优先遍历的时候,没遇到过 if !visited[from as usize] { visited[from as usize] = true; map[from as usize] = map[cur as usize] + 1; queue[r as usize] = from; r += 1; } add *= 10; } }}
fn get_min<T: Clone + Copy + std::cmp::PartialOrd>(a: T, b: T) -> T { if a < b { a } else { b }}
复制代码



代码用 solidity 编写。代码如下:


// SPDX-License-Identifier: MITpragma solidity ^0.8.17;
contract Hello{ function main() public pure returns (int32[] memory){ int32 n = 5; int32 m = 11; int32[] memory arr = new int32[](5); arr[0] = 1; arr[1] = 3; arr[2] = 5; arr[3] = 7; arr[4] = 9; int32[] memory ans = times(n,m,arr); return ans; }
function times(int32 n,int32 m,int32[] memory arr) public pure returns (int32[] memory){ // map[i] : i这个余数变成余数0,需要至少操作几次? uint mm=uint(uint32(m)); int32[] memory map = new int32[](mm); bfs(m, map); uint nn=uint(uint32(n)); int32[] memory ans = new int32[](nn); for (uint i = 0; i < nn; i++) { int32 num = arr[i]; int32 minTimes = 2147483647; if (num < m) { minTimes = map[uint(uint32(num))]; } else { for (int64 add = 1; add <= 1000000000; add *= 10) { int32 mod = int32((int64(num) + add) % int64(m)); minTimes = getMin(minTimes, map[uint(uint32(mod))] + 1); } } ans[i] = minTimes; } return ans; }
function getMin(int32 a,int32 b) public pure returns (int32){ if(a<b){ return a; }else{ return b; } }
function bfs(int32 m,int32[] memory map) public pure{ uint mm=uint(uint32(m)); bool[] memory visited = new bool[](mm); visited[0]=true; int32[] memory queue = new int32[](mm); int32 l = 0; int32 r = 1; // map[0] == 0 // 表示余数0变成余数0,需要至少0次 // 0进队列了, queue[0] = 0 // 0算访问过了,visited[0] = true while(l<r){ // 当前弹出的余数是cur int32 cur = queue[uint(uint32(l))]; l++; // 能加的数字,从1枚举到10^9 for(int64 add = 1;add<=1000000000;add*=10){ // 比如,m == 7 // 当前余数是cur,cur变成余数0,至少要a次 // 我们想知道 : (哪个余数b + add) % m == cur // 比如,add=10的时候,cur==5的时候 // 我们想知道 : (哪个余数b + 10) % 7 == 5 // 因为10 % 7 = 3 // 所以其实我们在求 : 哪个余数b + 3 == 5 // 显然b = 5 - 3 = cur - (add % m) = 2 // 再比如,add=10的时候,cur==2的时候 // 我们想知道 : (哪个余数b + 10) % 7 == 2 // 因为10 % 7 = 3 // 所以其实我们在求 : 哪个余数b + 3 == 2 // 这明显是不对的, // 所以其实我们在求 : 哪个余数b + 3 == 2 + m == 9 // 也就是b,通过加了add % m,来到了m + cur,多转了一圈 // b = 9 - 3 = cur - (add % m) + m = 6 // 也就是说,b = cur - (add % m), // 如果不小于0,那就是这个b,是我们要找的余数 // 如果小于0,那就是b+m,是我们要找的余数 int32 from = cur - int32(add%int64(m)); if (from < 0) { from += m; } // 这个余数我们终于找到了,因为cur变成余数0,需要a次 // 所以这个余数变成余数0,需要a+1次 // 当然前提是这个余数,之前宽度优先遍历的时候,没遇到过 if (!visited[uint(uint32(from))]) { visited[uint(uint32(from))] = true; map[uint(uint32(from))] = map[uint(uint32(cur))] + 1; queue[uint(uint32(r))] = from; r++; } } } }
}
复制代码




发布于: 2023-01-02阅读数: 2
用户头像

公众号:福大大架构师每日一题 2021-02-15 加入

公众号:福大大架构师每日一题

评论

发布
暂无评论
2023-01-02:某天,小美在玩一款游戏,游戏开始时,有n台机器, 每台机器都有一个能量水平,分别为a1、a2、…、an, 小美每次操作可以选其中的一台机器,假设选的是第i台, 那小美可以将其变成_算法_福大大架构师每日一题_InfoQ写作社区