From 461f8a9a5d33558a55d6dec65eff3d14378af55c Mon Sep 17 00:00:00 2001 From: YDZ Date: Fri, 6 Dec 2019 11:48:35 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20problem=20991?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Algorithms/0887. Super Egg Drop/README.md | 32 +++++++---- .../911. Online Election.go | 38 +++++++++++++ .../911. Online Election_test.go | 28 ++++++++++ Algorithms/0911. Online Election/README.md | 53 +++++++++++++++++++ 4 files changed, 140 insertions(+), 11 deletions(-) create mode 100644 Algorithms/0911. Online Election/911. Online Election.go create mode 100644 Algorithms/0911. Online Election/911. Online Election_test.go create mode 100755 Algorithms/0911. Online Election/README.md diff --git a/Algorithms/0887. Super Egg Drop/README.md b/Algorithms/0887. Super Egg Drop/README.md index 7ea30b87..dc1d32ec 100755 --- a/Algorithms/0887. Super Egg Drop/README.md +++ b/Algorithms/0887. Super Egg Drop/README.md @@ -59,7 +59,8 @@ What is the minimum number of moves that you need to know with certainty what  - 这一题如果按照题意正向考虑,动态规划的状态转移方程是 `searchTime(K, N) = max( searchTime(K-1, X-1), searchTime(K, N-X) )`。其中 `X` 是丢鸡蛋的楼层。随着 `X` 从 `[1,N]`,都能计算出一个 `searchTime` 的值,在所有这 `N` 个值之中,取最小值就是本题的答案了。这个解法可以 AC 这道题。不过这个解法不细展开了。时间复杂度 `O(k*N^2)`。

-

+

+ - 换个角度来看这个问题,定义 `dp[k][m]` 代表 `K` 个鸡蛋,`M` 次移动能检查的最大楼层。考虑某一步 `t` 应该在哪一层丢鸡蛋呢?一个正确的选择是在 `dp[k-1][t-1] + 1` 层丢鸡蛋,结果分两种情况: 1. 如果鸡蛋碎了,我们首先排除了该层以上的所有楼层(不管这个楼有多高),而对于剩下的 `dp[k-1][t-1]` 层楼,我们一定能用 `k-1` 个鸡蛋在 `t-1` 步内求解。因此这种情况下,我们总共可以求解无限高的楼层。可见,这是一种非常好的情况,但并不总是发生。 2. 如果鸡蛋没碎,我们首先排除了该层以下的 `dp[k-1][t-1]` 层楼,此时我们还有 `k` 个蛋和 `t-1` 步,那么我们去该层以上的楼层继续测得 `dp[k][t-1]` 层楼。因此这种情况下,我们总共可以求解 `dp[k-1][t-1] + 1 + dp[k][t-1]` 层楼。 @@ -68,33 +69,42 @@ What is the minimum number of moves that you need to know with certainty what  1. 如果在更低的楼层丢鸡蛋也能保证找到安全楼层。那么得到的结果一定不是最小步数。因为这次丢鸡蛋没有充分的展现鸡蛋和移动次数的潜力,最终求解一定会有鸡蛋和步数剩余,即不是能探测的最大楼层了。 2. 如果在更高的楼层丢鸡蛋,假设是第 `dp[k-1][t-1] + 2` 层丢鸡蛋,如果这次鸡蛋碎了,剩下 `k-1` 个鸡蛋和 `t-1` 步只能保证验证 `dp[k-1][t-1]` 的楼层,这里还剩**第** `dp[k-1][t-1]+ 1` 的楼层,不能保证最终一定能找到安全楼层了。 - 用反证法就能得出每一步都应该在第 `dp[k-1][t-1] + 1` 层丢鸡蛋。 -- 这道题还可以用二分搜索来解答。回到上面分析的状态转移方程:`dp[k][m] = dp[k-1][m-1] + dp[k][m-1] + 1` 。用数学方法来解析这个递推关系。令 `f(t,k)` 为 `t` 和 `k` 的函数,题目所要求能测到最大楼层是 `N` 的最小步数,即要求出 `f(t,k) ≥ N` 时候的最小 `t`。由状态转移方程可以知道:`f(t,k) = f(t-1,k) + f(t-1,k-1) + 1`,当 `k = 1` 的时候,对应一个鸡蛋的情况,`f(t,1) = t`,当 `t = 1` 的时候,对应一步的情况,`f(1,k) = 1`。有状态转移方程得: +- 这道题还可以用二分搜索来解答。回到上面分析的状态转移方程:`dp[k][m] = dp[k-1][m-1] + dp[k][m-1] + 1` 。用数学方法来解析这个递推关系。令 `f(t,k)` 为 `t` 和 `k` 的函数,题目所要求能测到最大楼层是 `N` 的最小步数,即要求出 `f(t,k) ≥ N` 时候的最小 `t`。由状态转移方程可以知道:`f(t,k) = f(t-1,k) + f(t-1,k-1) + 1`,当 `k = 1` 的时候,对应一个鸡蛋的情况,`f(t,1) = t`,当 `t = 1` 的时候,对应一步的情况,`f(1,k) = 1`。有状态转移方程得:

-

- 令 `g(t,k) = f(t,k) - f(t,k-1)`,可以得到: +

+ +- 令 `g(t,k) = f(t,k) - f(t,k-1)`,可以得到: +

-

- 可以知道 `g(t,k)` 是一个杨辉三角,即二项式系数: +

+ +- 可以知道 `g(t,k)` 是一个杨辉三角,即二项式系数: +

- 利用裂项相消的方法: + +- 利用裂项相消的方法:

- 于是可以得到: + +- 于是可以得到:

- 其中: + +- 其中:

- 于是针对每一项的二项式常数,都可以由前一项乘以一个分数得到下一项。 + +- 于是针对每一项的二项式常数,都可以由前一项乘以一个分数得到下一项。

- 利用二分搜索,不断的二分 `t`,直到逼近找到 `f(t,k) ≥ N` 时候最小的 `t`。时间复杂度 `O(K * log N)`,空间复杂度 `O(1)`。 + +- 利用二分搜索,不断的二分 `t`,直到逼近找到 `f(t,k) ≥ N` 时候最小的 `t`。时间复杂度 `O(K * log N)`,空间复杂度 `O(1)`。 diff --git a/Algorithms/0911. Online Election/911. Online Election.go b/Algorithms/0911. Online Election/911. Online Election.go new file mode 100644 index 00000000..cd6b5751 --- /dev/null +++ b/Algorithms/0911. Online Election/911. Online Election.go @@ -0,0 +1,38 @@ +package leetcode + +import ( + "sort" +) + +// TopVotedCandidate define +type TopVotedCandidate struct { + persons []int + times []int +} + +// Constructor911 define +func Constructor911(persons []int, times []int) TopVotedCandidate { + leaders, votes := make([]int, len(persons)), make([]int, len(persons)) + leader := persons[0] + for i := 0; i < len(persons); i++ { + p := persons[i] + votes[p]++ + if votes[p] >= votes[leader] { + leader = p + } + leaders[i] = leader + } + return TopVotedCandidate{persons: leaders, times: times} +} + +// Q define +func (tvc *TopVotedCandidate) Q(t int) int { + i := sort.Search(len(tvc.times), func(p int) bool { return tvc.times[p] > t }) + return tvc.persons[i-1] +} + +/** + * Your TopVotedCandidate object will be instantiated and called as such: + * obj := Constructor(persons, times); + * param_1 := obj.Q(t); + */ diff --git a/Algorithms/0911. Online Election/911. Online Election_test.go b/Algorithms/0911. Online Election/911. Online Election_test.go new file mode 100644 index 00000000..e2ee112c --- /dev/null +++ b/Algorithms/0911. Online Election/911. Online Election_test.go @@ -0,0 +1,28 @@ +package leetcode + +import ( + "fmt" + "testing" +) + +func Test_Problem911(t *testing.T) { + obj := Constructor911([]int{0, 1, 1, 0, 0, 1, 0}, []int{0, 5, 10, 15, 20, 25, 30}) + fmt.Printf("obj.Q[3] = %v\n", obj.Q(3)) // 0 + fmt.Printf("obj.Q[12] = %v\n", obj.Q(12)) // 1 + fmt.Printf("obj.Q[25] = %v\n", obj.Q(25)) // 1 + fmt.Printf("obj.Q[15] = %v\n", obj.Q(15)) // 0 + fmt.Printf("obj.Q[24] = %v\n", obj.Q(24)) // 0 + fmt.Printf("obj.Q[8] = %v\n", obj.Q(8)) // 1 + + obj = Constructor911([]int{0, 0, 0, 0, 1}, []int{0, 6, 39, 52, 75}) + fmt.Printf("obj.Q[45] = %v\n", obj.Q(45)) // 0 + fmt.Printf("obj.Q[49] = %v\n", obj.Q(49)) // 0 + fmt.Printf("obj.Q[59] = %v\n", obj.Q(59)) // 0 + fmt.Printf("obj.Q[68] = %v\n", obj.Q(68)) // 0 + fmt.Printf("obj.Q[42] = %v\n", obj.Q(42)) // 0 + fmt.Printf("obj.Q[37] = %v\n", obj.Q(37)) // 0 + fmt.Printf("obj.Q[99] = %v\n", obj.Q(99)) // 0 + fmt.Printf("obj.Q[26] = %v\n", obj.Q(26)) // 0 + fmt.Printf("obj.Q[78] = %v\n", obj.Q(78)) // 0 + fmt.Printf("obj.Q[43] = %v\n", obj.Q(43)) // 0 +} diff --git a/Algorithms/0911. Online Election/README.md b/Algorithms/0911. Online Election/README.md new file mode 100755 index 00000000..19b8607e --- /dev/null +++ b/Algorithms/0911. Online Election/README.md @@ -0,0 +1,53 @@ +# [911. Online Election](https://leetcode.com/problems/online-election/) + + +## 题目: + +In an election, the `i`-th vote was cast for `persons[i]` at time `times[i]`. + +Now, we would like to implement the following query function: `TopVotedCandidate.q(int t)` will return the number of the person that was leading the election at time `t`. + +Votes cast at time `t` will count towards our query. In the case of a tie, the most recent vote (among tied candidates) wins. + +**Example 1:** + + Input: ["TopVotedCandidate","q","q","q","q","q","q"], [[[0,1,1,0,0,1,0],[0,5,10,15,20,25,30]],[3],[12],[25],[15],[24],[8]] + Output: [null,0,1,1,0,0,1] + Explanation: + At time 3, the votes are [0], and 0 is leading. + At time 12, the votes are [0,1,1], and 1 is leading. + At time 25, the votes are [0,1,1,0,0,1], and 1 is leading (as ties go to the most recent vote.) + This continues for 3 more queries at time 15, 24, and 8. + +**Note:** + +1. `1 <= persons.length = times.length <= 5000` +2. `0 <= persons[i] <= persons.length` +3. `times` is a strictly increasing array with all elements in `[0, 10^9]`. +4. `TopVotedCandidate.q` is called at most `10000` times per test case. +5. `TopVotedCandidate.q(int t)` is always called with `t >= times[0]`. + + +## 题目大意 + +在选举中,第 i 张票是在时间为 times[i] 时投给 persons[i] 的。 + +现在,我们想要实现下面的查询函数: TopVotedCandidate.q(int t) 将返回在 t 时刻主导选举的候选人的编号。 + +在 t 时刻投出的选票也将被计入我们的查询之中。在平局的情况下,最近获得投票的候选人将会获胜。 + +提示: + +1. 1 <= persons.length = times.length <= 5000 +2. 0 <= persons[i] <= persons.length +3. times 是严格递增的数组,所有元素都在 [0, 10^9] 范围中。 +4. 每个测试用例最多调用 10000 次 TopVotedCandidate.q。 +5. TopVotedCandidate.q(int t) 被调用时总是满足 t >= times[0]。 + + + + +## 解题思路 + +- 给出一个 2 个数组,分别代表第 `i` 人在第 `t` 时刻获得的票数。需要实现一个查询功能的函数,查询在任意 `t` 时刻,输出谁的选票领先。 +- `persons[]` 数组里面装的是获得选票人的编号,`times[]` 数组里面对应的是每个选票的时刻。`times[]` 数组默认是有序的,从小到大排列。先计算出每个时刻哪个人选票领先,放在一个数组中,实现查询函数的时候,只需要先对 `times[]` 数组二分搜索,找到比查询时间 `t` 小的最大时刻 `i`,再在选票领先的数组里面输出对应时刻领先的人的编号即可。