[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];
    }
}
原文地址:https://www.cnblogs.com/lz87/p/12820995.html