Clean Java
DFS + memo, we use dp[curHandElement, remainingElement] to cache\n\nremainingElement use Arrays.hashCode(count) to save\n\nlogic is pretty straight forward, we 
DFS + memo, we use dp[curHandElement, remainingElement] to cache\n\nremainingElement use Arrays.hashCode(count) to save\n\nlogic is pretty straight forward, we pick number one by one, and when we meet the size N of one bucket, we clean the numbers at hand and start picking again.\n\nversion1: hasSet version (118ms)\n```\nclass Solution {\n    int N;\n    Map<String, Integer> memo;\n    int k;\n    public int minimumIncompatibility(int[] nums, int k) {\n        Arrays.sort(nums);\n        memo = new HashMap<>(); k = k;\n        int[] count = new int[17];\n        for (int num : nums) if (++count[num] > k) return -1;  // if same number more than k times, we can not answer the question cause there must be one bucket with same number twice\n        N = nums.length / k;   //N is the number of element in each bucket\n        return dfs(new HashSet<>(), count);\n    }\n    \n    private int dfs(Set<Integer> level, int[] count) {\n        if (level.size() == N)\n            return end(count) ? findDiff(level) : findDiff(level) + dfs(new HashSet<>(), count);\n        String key = Arrays.hashCode(count) + " " + level; //count is remaining element status, level is current hand bucket element\n        if (memo.containsKey(key)) return memo.get(key);\n        int res = 1000;\n        for (int i = 1; i <= 16; i++) {\n            if (count[i] <= 0) continue; //no more this number left\n            if (!level.add(i)) continue; //at hand same number already exist\n            count[i]--;\n            res = Math.min(res, dfs(level, count));\n            count[i]++;\n            level.remove(i);\n            if (level.size() == 0) break;   // first element we don\'t need to expand\n        }\n        memo.put(key, res);\n        return res;\n    }\n    \n    private int findDiff(Set<Integer> level) {\n        return Collections.max(level) - Collections.min(level);\n    }\n    \n    private boolean end(int[] count) {\n        for (int c : count) if (c != 0) return false;\n        return true;\n    }\n}\n\n```\n\nversion2:  bitMask version (59ms)\n\ncurrent hand element use bitMask to save\n\n```\nclass Solution {\n    int N;\n    Map<String, Integer> memo;\n    int k;\n    public int minimumIncompatibility(int[] nums, int k) {\n        Arrays.sort(nums);\n        memo = new HashMap<>(); k = k;\n        int[] count = new int[17];\n        for (int num : nums) if (++count[num] > k) return -1;\n        N = nums.length / k;\n        return dfs(0, count);\n    }\n    \n    private int dfs(int level, int[] count) {\n        if (Integer.bitCount(level) == N) {\n            if (end(count)) return findDiff(level);\n            else return findDiff(level) + dfs(0, count);\n        } \n        String key = Arrays.hashCode(count) + " " + level;\n        if (memo.containsKey(key)) return memo.get(key);\n        int res = 1000;\n        for (int i = 1; i <= 16; i++) {\n            if (count[i] <= 0) continue;\n            int mask = 1 << i;\n            if ((level & mask) != 0) continue;\n            level |= mask;\n            count[i]--;\n            res = Math.min(res, dfs(level, count));\n            count[i]++;\n            level ^= mask;\n            if (Integer.bitCount(level) == 0) break;  // first element we don\'t need to expand\n        }\n        memo.put(key, res);\n        return res;\n    }\n    \n    private int findDiff(int level) {\n        int max = 0, min = 16;\n        for (int i = 1; i <= 16; i++) {\n            int mask = 1 << i;\n            if ((level & mask) == 0) continue;\n            max = Math.max(max, i);\n            min = Math.min(min, i);\n        }\n        return max - min;\n    }\n    \n    private boolean end(int[] count) {\n        for (int c : count) if (c != 0) return false;\n        return true;\n    }\n}\n\n\n```
[]