LeetCode 1230 - Toss Strange Coins


http://leetcode.liangjiateng.cn/leetcode/toss-strange-coins/description
You have some coins.  The i-th coin has a probability prob[i] of facing heads when tossed.
Return the probability that the number of coins facing heads equals target if you toss every coin exactly once.

Example 1:
Input: prob = [0.4], target = 1
Output: 0.40000
Example 2:
Input: prob = [0.5,0.5,0.5,0.5,0.5], target = 0
Output: 0.03125

Constraints:
  • 1 <= prob.length <= 1000
  • 0 <= prob[i] <= 1
  • 0 <= target <= prob.length
  • Answers will be accepted as correct if they are within 10^-5 of the correct answer.

https://coordinate.wang/index.php/archives/2693/
有一些不规则的硬币。在这些硬币中,prob[i] 表示第 i 枚硬币正面朝上的概率。
请对每一枚硬币抛掷 一次,然后返回正面朝上的硬币数等于 target 的概率。
示例 1:
输入:prob = [0.4], target = 1
输出:0.40000
示例 2:
输入:prob = [0.5,0.5,0.5,0.5,0.5], target = 0
输出:0.03125 
提示:
  • 1 <= prob.length <= 1000
  • 0 <= prob[i] <= 1
  • 0 <= target <= prob.length
  • 如果答案与标准答案的误差在 10^-5 内,则被视为正确答案。
X. 
dp[i][j] := prob of j coins face up after tossing first i coins.
dp[i][j] = dp[i-1][j] * (1 – p[i]) + dp[i-1][j-1] * p[i]

Time complexity: O(n^2)
Space complexity: O(n^2) -> O(n)

  double probabilityOfHeads(vector<double>& prob, int target) {
    const int n = prob.size();
    vector<double> dp(target + 1);    
    dp[0] = 1.0;
    for (int i = 0; i < n; ++i)
      for (int j = min(i + 1, target); j >= 0; --j)
        dp[j] = dp[j] * (1 - prob[i]) + (j > 0 ? dp[j - 1] : 0) * prob[i];
    return dp[target];
  }

Solution 2: Recursion + Memorization
Time complexity: O(n^2)
Space complexity: O(n^2)
  double probabilityOfHeads(vector<double>& prob, int target) {    
    vector<vector<double>> mem(prob.size() + 1, vector<double>(target + 1, -1));
    function<double(int, int)> dp = [&](int n, int k) {
      if (k > n || k < 0) return 0.0;      
      if (n == 0) return 1.0;
      if (mem[n][k] >= 0) return mem[n][k];
      const double p = prob[n - 1];
      return mem[n][k] = p * dp(n - 1, k - 1) + (1 - p) * dp(n - 1, k);
    };    
    return dp(prob.size(), target);
  }
动态规划。首先假设dp[i][j]为投完第i枚硬币后有j枚硬币朝上的概率。如果第i-1枚硬币投完后有j枚朝上,那么第i枚硬币就只能朝下;如果i-1枚硬币投完后有j-1枚朝上,那么第i枚硬币就只能朝上。很容易建立状态转移方程,dp[i][j] = dp[i-1][j-1] * prob[i] + dp[i-1][j] * (1 - prob[i])。
(动态规划) O(n×(target+1))
  1. f(i,j) 表示掷了前 i 个硬币,正面朝上次数为 j 的概率。
  2. 初始时,f(0,0)=1,其余为 0。
  3. 转移时,枚举这一次的结果,正面则 f(i,j)=f(i,j)+f(i1,j1)prob[i],反面 f(i,j)=f(i,j)+f(i1,j)(1prob[i])。注意这里的 prob 的下标是从 1 开始的。
  4. 最终答案为 f(n,target)

时间复杂度

  • 状态数共有 O(n×(target+1)) 个,转移时间为常数,故总时间复杂度为 O(n×(target+1))

空间复杂度

  • 和状态数相同,故时间复杂度为 O(n×(target+1))
  • 可以通过滚动数组优化到 O(target+1)
double probabilityOfHeads(vector<double>& prob, int target) { int n = prob.size(); vector<vector<double>> f(n + 1, vector<double>(target + 1, 0)); f[0][0] = 1; for (int i = 1; i <= n; i++) for (int j = 0; j <= target; j++) { if (j > 0) f[i][j] += f[i - 1][j - 1] * prob[i - 1]; f[i][j] += f[i - 1][j] * (1 - prob[i - 1]); } return f[n][target]; }


X.
实际上这是一个组合问题,首先不难想到dfs的写法,dfs的思路参考Leetcode 77:组合(最详细的解法!!!),定义函数f(u,k)表示在第u个位置之前有k个正面朝上的概率。
class Solution:
    def probabilityOfHeads(self, prob: List[float], target: int) -> float:
        n, res = len(prob), 0
        def dfs(u, k, state):
            nonlocal res
            if k == target:
                t = 1.0
                for i in range(n):
                    if state & (1 << i):
                        t *= prob[i]
                    else:
                        t *= (1 - prob[i])
                res += t
                return
            for i in range(u, n):
                state |= (1 << i)
                dfs(i + 1, k + 1, state)
                state -= (1 << i)
        
        dfs(0, 0, 0)
        return res
但是这么做超时了,那么加记忆化呗!但是加了记忆化依旧超时,为什么?实际上上面代码的写法没有将问题归纳为多个重叠子问题,也就是没有递推公式。递推公式可以表示为:
  • f(u,k)=prob[u]f(u+1,k+1)+(1prob[u])f(u,k+1)
按照这种思路去做的话,就可以写出如下代码。
from functools import lru_cache
class Solution:
    def probabilityOfHeads(self, prob: List[float], target: int) -> float:
        n = len(prob)
        @lru_cache(None)
        def dfs(u, k):
            if k > target:
                return 0.0
            if u == n:
                if k == target:
                    return 1.0
                else:
                    return 0.0
            
            res = 0
            res += prob[u] * dfs(u + 1, k + 1)
            res += (1 - prob[u]) * dfs(u + 1, k)
            return res
        
        return dfs(0, 0)
可以通过!!!这里我们就需要注意了,虽然都是dfs的写法,但是不同的写法对于结果的影响非常大。通过记忆化加dfs解决的问题,也可以通过动态规划来做。而且这还是一个经典的01背包问题,对于每个元素考虑放和不放两种状态,而背包的容积就是target
class Solution:
    def probabilityOfHeads(self, prob: List[float], target: int) -> float:
        dp = [1] + [0] * target
        for p in prob:
            for k in range(target, -1, -1):
                dp[k] = (dp[k - 1] if k else 0) * p + dp[k] * (1 - p)
        return dp[-1]

    public double probabilityOfHeads(double[] prob, int target) {
        if(target==0){
            double res = 1;
            for(int i=0;i<prob.length;i++){
                res *= (1 - prob[i]);
            }
            return res;
        }

        int N = prob.length;
        double[][] dp = new double[target+1][N];
        dp[0][0]=1-prob[0];
        dp[1][0]=prob[0];

        for(int j=1;j<N;j++){
            for(int i=0;i<=target;i++){
                dp[i][j]+=dp[i][j-1]*(1-prob[j]);
                if(i>0) {
                    dp[i][j] += dp[i - 1][j - 1] * prob[j];
                }
            }
        }
        return dp[target][N-1];
    }
这道题是一道典型的 dp 题,只需要按照一般 dp 的思路就行。
  1. 分析 dp 数组的坐标变量和范围。
  2. 分析最后返回的值在哪。
  3. 已知哪些值。
  4. 递推关系是什么?

Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts