zoukankan      html  css  js  c++  java
  • [LeetCode 1434] Number of Ways to Wear Different Hats to Each Other

    There are n people and 40 types of hats labeled from 1 to 40.

    Given a list of list of integers hats, where hats[i] is a list of all hats preferred by the i-th person.

    Return the number of ways that the n people wear different hats to each other.

    Since the answer may be too large, return it modulo 10^9 + 7.

     

    Example 1:

    Input: hats = [[3,4],[4,5],[5]]
    Output: 1
    Explanation: There is only one way to choose hats given the conditions. 
    First person choose hat 3, Second person choose hat 4 and last one hat 5.

    Example 2:

    Input: hats = [[3,5,1],[3,5]]
    Output: 4
    Explanation: There are 4 ways to choose hats
    (3,5), (5,3), (1,3) and (1,5)
    

    Example 3:

    Input: hats = [[1,2,3,4],[1,2,3,4],[1,2,3,4],[1,2,3,4]]
    Output: 24
    Explanation: Each person can choose hats labeled from 1 to 4.
    Number of Permutations of (1,2,3,4) = 24.
    

    Example 4:

    Input: hats = [[1,2,3],[2,3,5,6],[1,3,7,9],[1,8,9],[2,5,7]]
    Output: 111
    

     

    Constraints:

    • n == hats.length
    • 1 <= n <= 10
    • 1 <= hats[i].length <= 40
    • 1 <= hats[i][j] <= 40
    • hats[i] contains a list of unique integers.

    Given the constraints, we can infer this problem can be solved by dynamic programming + bitmask. The question is for hat and people, which one should we choose to use bitmask to represent its states? or should we use bitmask states on both. We can have up to 2^10 people states and 2^40 hat states, so the answer is obvious, we should choose people! Otherwise our runtime and space are going to explode as 2^40 is > 10^12, too much!

    So let's define dp[i] as the number of ways to have all picked people in state i all wear distinct hats. Then we loop through all possible hats, for each hat that is being added, loop through all possible people states, for each state, if there are unpicked people that can wear the current hat, assign the current hat to this person and update the number of ways of the new state with this new assignment. After going through all hats, the final answer is dp[2^n - 1]. And we initialize dp[0] to 1, representing there is 1 way to assign no hats to 0 people.

    There is a tricky cavet though! When looping through all people states, we must do it in decreasing order, not increasing order.

    Consider the given example 1, 

    if we loop people states from 000 to 111, then after processing the hat 3, dp[001] = 1, dp[010] = 0, dp[011] = 0. Then we add hat 4 into consideration. We first update dp[001] from 1 to 2, representing we can assign either hat 3 or 4 to people 1. Then update dp[010] from 0 to 1, representing we can assign hat 4 to people 2. Now Time to update dp[011] by using dp[001] and dp[010], which is 2 + 1 = 3. This is wrong! When we only have hat 3 and 4 available, the state 011 should be 1 as we only have 1 way to assign, that is hat 3 to people 1 and hat 4 to people 2.  

    What actually happened?

    after updating dp[001] from 1 to 2, we are saying people 1 can have either hat 3 or 4. Then we try to assign hat 4 to people 2(this makes the state transition from 001 to 011), we claim that we can just blindly assign hat 4 to people 2 while still insisting people 1 can still have hat 3 or 4! See the paradox now? 

    similarly, after updating dp[010] from 0 to 1, we are saying people 2 can have hat 4. Then we try to assign hat 4 to people 1(this makes the state transition from 001 to 011), we claim that we can just blindly assign hat 4 to people 1 while still insisting people 2 can still have hat 4!

    If we loop through the people states in decreasing order,

    after processing hat 3, dp[001] = 1, dp[010] = 0, dp[011] = 0.

    dp[001] to dp[011]:  assign hat 4 to people 2, and we have 1 way to have some hats assigned to 001, so dp[011] is just 1, meaning hat 3 to people 1 and hat 4 to people 2.

    dp[010] to dp[011]: assign hat 4 to people 1, and we have 0 way to have some hats assigned to 010, so no real update for dp[011]. Assigning hat 4 to people 1 or 2 will happen later when we are at state 000 since we loop over people states from 111 to 000. 

    The key here is that for a state i , we use its previous iteration's result to update new states, the update for state i itself happens later when we are at an even smaller state that can transite to state i by flipping a bit 0 to 1.

    class Solution {
        public int numberWays(List<List<Integer>> hats) {
            int mod = (int)1e9 + 7;
            int n = hats.size();
            long[] dp = new long[1 << n];
            Map<Integer, Set<Integer>> hatToPpl = new HashMap<>();
            Set<Integer> set = new HashSet<>();
            for(int i = 0; i < hats.size(); i++) {
                for(int j = 0; j < hats.get(i).size(); j++) {
                    hatToPpl.computeIfAbsent(hats.get(i).get(j), k -> new HashSet<>()).add(i);
                    set.add(hats.get(i).get(j));
                }
            }
            List<Integer> allHats = new ArrayList<>(set);
            dp[0] = 1L;
            
            for(int i = 0; i < allHats.size(); i++) {
                for(int j = dp.length - 1; j >= 0; j--) {
                    for(int ppl : hatToPpl.get(allHats.get(i))) {
                        if((j & (1 << ppl)) == 0) {
                            int newState = j | (1 << ppl);
                            dp[newState] = (dp[newState] + dp[j]) % mod;
                        }
                    }
                }
            }
            return (int)dp[(1 << n) - 1];
        }
    }
  • 相关阅读:
    b_lc_长度为 3 的不同回文子序列(统计两个相同字符中间有多少个不同字符)
    b_lc_最小未被占据椅子的编号(记录每个时间来的人 + pq)
    b_lc_统计好数字的数量(排列数+组合数+快速幂)
    TreeMap
    LinkedHashMap
    HashMap的总结
    HashMap
    Collection
    Map
    LinkedList学习
  • 原文地址:https://www.cnblogs.com/lz87/p/12820995.html
Copyright © 2011-2022 走看看