2024-06-03
题目: 1103. 分糖果 II
题解和思路
直接模拟,根据题意给每个小朋友分配对应的糖果即可。
定义 i = 0 i=0 i = 0 ,表示每个小朋友要分配的糖果,第 i m o d n i\mod n i mod n 个小朋友将分配 i + 1 i+1 i + 1 颗糖果,当糖果不够分的时候,直接将剩余的糖果全部给当前的小朋友即可
1 2 3 4 5 6 7 8 func distributeCandies (candies int , n int ) []int { ans := make ([]int , n) for i := 0 ; candies > 0 ; i++ { ans[i%n] += min(candies, i+1 ) candies -= i + 1 } return ans }
2024-06-04
题目: 3067. 在带权树网络中统计可连接服务器对数目
题解和思路
首先通过 d f s dfs df s 枚举以节点 a a a 为跟的树有多少个符合要求的节点,假设存在三棵树,三棵树是有序地,从左到右依次为(a 1 , a 2 , a 3 a_1, a_2,a_3 a 1 , a 2 , a 3 ),树中满足要求的节点分别为 ( 3 , 4 , 5 ) (3,4,5) ( 3 , 4 , 5 ) 。
a 2 a_2 a 2 左边的节点只有 a 1 a_1 a 1 ,两两相连得到 3 × 4 3 \times 4 3 × 4
a 3 a3 a 3 左边的节点有 a 1 , a 2 a_1, a_2 a 1 , a 2 ,两两相连得到 ( 3 + 4 ) × 5 (3+4) \times 5 ( 3 + 4 ) × 5
只从左往右开,这样可以避开重复的对,题目中要求 a < b a < b a < b ,即不允许出现重复
遍历每个节点,统计最终符合要求的最终节点,其中如果节点 a a a 只有一个子树可以跳过。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 type edge struct { to, weight int } func countPairsOfConnectableServers (edges [][]int , signalSpeed int ) []int { n := len (edges) + 1 g := make ([][]edge, n) for _, e := range edges { u, v, w := e[0 ], e[1 ], e[2 ] g[u] = append (g[u], edge{v, w}) g[v] = append (g[v], edge{u, w}) } var dfs func (int , int , int ) int dfs = func (to, from, sum int ) int { cnt := 0 if sum%signalSpeed == 0 { cnt = 1 } for _, e := range g[to] { if e.to != from { cnt += dfs(e.to, to, sum+e.weight) } } return cnt } res := make ([]int , n) for i, gi := range g { if len (gi) == 1 { continue } s := 0 for _, e := range gi { cnt := dfs(e.to, i, e.weight) res[i] += cnt * s s += cnt } } return res }
2024-06-05
题目: 3072. 将元素分配到两个数组中 II
题解和思路
主要是实现 g r e a t e r C o u n t greaterCount g re a t er C o u n t ,这部分我没搞懂,见树状数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 type fenwick []int func (f fenwick) add(i int ) { for ; i < len (f); i += i & -i { f[i]++ } } func (f fenwick) pre(i int ) (res int ) { for ; i > 0 ; i &= i - 1 { res += f[i] } return } func resultArray (nums []int ) (ans []int ) { sorted := slices.Clone(nums) slices.Sort(sorted) sorted = slices.Compact(sorted) m := len (sorted) a := nums[:1 ] b := []int {nums[1 ]} t1 := make (fenwick, m+1 ) t2 := make (fenwick, m+1 ) t1.add(sort.SearchInts(sorted, nums[0 ]) + 1 ) t2.add(sort.SearchInts(sorted, nums[1 ]) + 1 ) for _, x := range nums[2 :] { v := sort.SearchInts(sorted, x) + 1 gc1 := len (a) - t1.pre(v) gc2 := len (b) - t2.pre(v) if gc1 > gc2 || gc1 == gc2 && len (a) <= len (b) { a = append (a, x) t1.add(v) } else { b = append (b, x) t2.add(v) } } return append (a, b...) }
2024-06-06
题目: 2938. 区分黑球与白球
题解和思路
没有什么技巧,只能一个个移动,从左到右记录黑球数量 c n t cnt c n t ,当遇到白球时需要把白球左移 c n t cnt c n t 个才能移到左侧。遍历 s s s 的同时累加 c n t cnt c n t 即是答案。
1 2 3 4 5 6 7 8 9 10 11 12 func minimumSteps (s string ) int64 { ans := 0 cnt := 0 for _, c := range s { if c == '1' { cnt++ } else { ans += cnt } } return int64 (ans) }
2024-06-07
题目: 3038. 相同分数的最大操作数目 I
题解和思路
按照题意从左网友循环即可
1 2 3 4 5 6 7 8 func maxOperations (nums []int ) int { s := nums[0 ] + nums[1 ] ans := 1 for i := 3 ; i < len (nums) && nums[i-1 ]+nums[i] == s; i += 2 { ans++ } return ans }
2024-06-08
题目: 3040. 相同分数的最大操作数目 II
题解和思路
根据题意的操作,就是求子问题,且是两侧缩小的,确定了第一次删除的元素和,之后再通过三个中的操作继续递归即可。
定义 d f s ( i , j ) dfs(i,j) df s ( i , j ) 表示再区间 [ i , j ] [i,j] [ i , j ] 内连续子数组,最多可以执行多少次操作。递归终点 i > = j i>=j i >= j ,已经没有剩下的数,无法递归。
枚举三种操作模式,如果当前操作方式可行,继续向下枚举,即 d f s ( i + 2 , j ) , d f s ( i + 1 , j − 1 ) , d f s ( i , j + 2 ) dfs(i+2,j),dfs(i+1,j-1),dfs(i,j+2) df s ( i + 2 , j ) , df s ( i + 1 , j − 1 ) , df s ( i , j + 2 ) ,返回的结果 + 1 +1 + 1 ,再这三种结果中取最大值,即为 d f s ( i , j ) dfs(i,j) df s ( i , j ) ,如果三种都操作都不行,返回 0 0 0 。
根据三种操作,递归入口分别为,d f s ( 2 , n − 1 ) , d f s ( 0 , n − 3 ) , d f s ( 1 , n − 2 ) dfs(2, n-1), dfs(0, n-3), dfs(1, n-2) df s ( 2 , n − 1 ) , df s ( 0 , n − 3 ) , df s ( 1 , n − 2 ) 。因为三种初始化对应 t a r g e t target t a r g e t 不同,额外定义 h e l p e r helper h e lp er 为入口,在里面分别执行 d f s dfs df s 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 func maxOperations (nums []int ) int { n := len (nums) res1 := helper(nums[2 :], nums[0 ]+nums[1 ]) res2 := helper(nums[:n-2 ], nums[n-1 ]+nums[n-2 ]) res3 := helper(nums[1 :n-1 ], nums[0 ]+nums[n-1 ]) return max(res1, res2, res3) + 1 } func helper (a []int , target int ) int { n := len (a) memo := make ([][]int , n) for i := range memo { memo[i] = make ([]int , n) for j := range memo[i] { memo[i][j] = -1 } } var dfs func (int , int ) int dfs = func (i, j int ) int { if i >= j { return 0 } res := 0 if memo[i][j] != -1 { return memo[i][j] } if a[i]+a[i+1 ] == target { res = max(res, dfs(i+2 , j)+1 ) } if a[j]+a[j-1 ] == target { res = max(res, dfs(i, j-2 )+1 ) } if a[i]+a[j] == target { res = max(res, dfs(i+1 , j-1 )+1 ) } memo[i][j] = res + 1 return res } return dfs(0 , n-1 ) }
优化 :
最大的移动次数为 ⌊ n 2 ⌋ \lfloor \frac{n}{2} \rfloor ⌊ 2 n ⌋ ,当 r e s 1 res1 res 1 的结果为 ⌊ n 2 ⌋ \lfloor \frac{n}{2} \rfloor ⌊ 2 n ⌋ ,即递归到 i < = j i<=j i <= j 时,可以直接返回,因为后续操作不可能再大于 ⌊ n 2 ⌋ \lfloor \frac{n}{2} \rfloor ⌊ 2 n ⌋
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 func maxOperations3040 (nums []int ) int { n := len (nums) res1, done := helper(nums[2 :], nums[0 ]+nums[1 ]) if done { return n / 2 } res2, done := helper(nums[:n-2 ], nums[n-1 ]+nums[n-2 ]) if done { return n / 2 } res3, done := helper(nums[1 :n-1 ], nums[0 ]+nums[n-1 ]) if done { return n / 2 } return max(res1, res2, res3) + 1 } func helper (a []int , target int ) (int , bool ) { n := len (a) memo := make ([][]int , n) for i := range memo { memo[i] = make ([]int , n) for j := range memo[i] { memo[i][j] = -1 } } var done bool var dfs func (int , int ) int dfs = func (i, j int ) int { if done { return 0 } if i >= j { done = true return 0 } res := 0 if memo[i][j] != -1 { return memo[i][j] } if a[i]+a[i+1 ] == target { res = max(res, dfs(i+2 , j)+1 ) } if a[j]+a[j-1 ] == target { res = max(res, dfs(i, j-2 )+1 ) } if a[i]+a[j] == target { res = max(res, dfs(i+1 , j-1 )+1 ) } memo[i][j] = res return res } return dfs(0 , n-1 ), done }
2024-06-09
题目: 312. 戳气球
题解和思路
首尾气球戳破时旁边没有气球按照 1 1 1 处理,可以理解为 n u m s [ − 1 ] nums[-1] n u m s [ − 1 ] 和 n u m s [ n + 1 ] nums[n+1] n u m s [ n + 1 ] 的值都为 1 1 1 。重新定义数组 p o i n t s points p o in t s 存储 n u m s nums n u m s 和首尾的虚拟气球
使用动态规划思路,定义 d p dp d p 数组即可,其中 d p [ i ] [ j ] = x dp[i][j]=x d p [ i ] [ j ] = x ,表示戳破气球 i i i 到气球 j j j 之间(不包括 i i i 和 j j j )的所有气球,最高得分为 x x x 。因为这两段的气球是虚拟的,所以不需要戳破,最终我们的答案为就是 d p [ 0 ] [ n + 1 ] dp[0][n+1] d p [ 0 ] [ n + 1 ] 的值
假设最后戳破的一个气球为 k ( i < k < j ) k(i<k<j) k ( i < k < j ) ,要戳破 k k k ,就需要把 ( i , k ) (i,k) ( i , k ) 和 ( k , j ) (k,j) ( k , j ) 两个区间的气球都戳破,最后剩下气球 k k k ,相邻的气球就是 i i i 和 j j j ,此时戳破 k k k 的得分就是 p o i n t s [ i ] × p o i n t s [ k ] × p o i n t s [ j ] points[i]\times points[k]\times points[j] p o in t s [ i ] × p o in t s [ k ] × p o in t s [ j ] 。当 k k k 被戳破,( i , j ) (i,j) ( i , j ) 之间所有的气球都被戳破,而此时 d p [ i ] [ j ] = d p [ i ] [ k ] + d p [ k ] [ j ] + p o i n t s [ i ] × p o i n t s [ j ] × p o i n t s [ k ] dp[i][j] = dp[i][k]+dp[k][j]+points[i] \times points[j]\times points[k] d p [ i ] [ j ] = d p [ i ] [ k ] + d p [ k ] [ j ] + p o in t s [ i ] × p o in t s [ j ] × p o in t s [ k ] ,这就是状态转移方程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 func maxCoins (nums []int ) int { n := len (nums) points := make ([]int , n+2 ) points[0 ], points[n+1 ] = 1 , 1 copy (points[1 :n+1 ], nums) dp := make ([][]int , n+2 ) for i := range dp { dp[i] = make ([]int , n+2 ) } for i := n; i >= 0 ; i-- { for j := i + 1 ; j < n+2 ; j++ { for k := i + 1 ; k < j; k++ { dp[i][j] = max(dp[i][j], dp[i][k]+dp[k][j]+points[i]*points[j]*points[k]) } } } return dp[0 ][n+1 ] }