一、题目描述
题目链接:https://leetcode-cn.com/problems/number-of-ways-to-wear-different-hats-to-each-other/
二、题目分析
emm这道题思路其实还是不难的,但是优化还是比较难想。本菜鸡就写一下自己的想法然后再看看别人是怎么写的最终通过吧。
首先一个最直观的想法,就是使用dfs列举所有可能,然后计数。而且我们很容易能想到其实在搜索的过程中会有很多重复使用到的值。例如A喜欢2,3,4帽子,B喜欢2,3,4帽子,C喜欢1,2,3帽子。假设我们第一次给A一顶2号帽子,给B一顶3号帽子,第二次给A一顶3号帽子,给B一顶2号帽子,其实这两种情况C能选择的情况都是一样的,因此我们可以使用一个memo来进行记忆。我的想法是使用一个数组used来记录帽子的使用情况,将used转成String再拼接上“#”+当前人index作为key,进行一个记录。但是还是超时了。
看了一些题解之后发现其实用一个二维数组就可以记录了,但是又有一个问题,帽子的数量是40,若用位(bit)来表示是超过范围的(int 32位)看了一些题解之后才知道可以转变一下思路,因为人数不超过10,因此可以将帽子发给人,这样就是将人用位(bit)来标示,就可以记录状态了。详情请见代码。代码来自:https://leetcode-cn.com/problems/number-of-ways-to-wear-different-hats-to-each-other/solution/javashuang-bai-by-laboum/
三、代码
我的渣渣代码
public int numberWays(List<List<Integer>> hats) {
int[] used = new int[41];
return numberWaysDFS(used, hats, 0, new HashMap<>());
}
private int numberWaysDFS(int[] used, List<List<Integer>> hats, int index, HashMap<String,Integer> dp) {
if (index == hats.size()) {
return 1;
}
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(index).append("#").append(Arrays.toString(used));
String key = stringBuilder.toString();
if (dp.containsKey(key)) return dp.get(key);
int count = 0;
for (int hat: hats.get(index)) {
if (used[hat] == 0) {
used[hat] = 1;
count += numberWaysDFS(used, hats, index+1, dp);
used[hat] = 0;
}
}
dp.put(key, count);
return count;
}
大佬代码
public int numberWays2(List<List<Integer>> hats) {
int n = hats.size();
int N = 1000000007;
// 找到帽子的最大序号,用于构建like
int maxHat = 0;
for (List<Integer> hat: hats) {
for (int i: hat) {
maxHat = Math.max(maxHat, i);
}
}
//用于记录i号帽子有多少人喜欢
List<List<Integer>> like = new ArrayList<>();
for (int i = 0; i < maxHat; i ++ ) {
like.add(new ArrayList<>());
}
for (int i = 0; i < n; i ++ ) {
for (int hat: hats.get(i)) {
like.get(hat-1).add(i);
}
}
// 方案数量(指每个人有没有戴帽子)
int total = (1<<n);
/dp[i][j] 表示前i个人(分帽子状态为j)的情况下方案数
//(j考虑为一个二进制数,第k位上的01表示第k个人有无分到帽子。j就是一个方案,总方案数为2^n即1<<n)
int[][] dp = new int[maxHat+1][total];
dp[0][0] = 1;
for (int i = 1; i <= maxHat; i ++ ) {
// 列举所有人戴帽子的情况(1没有戴,0有戴)
for (int j = 0; j < total; j ++ ) {
// 第i顶帽子不分配的情况
dp[i][j] = dp[i-1][j];
// 遍历所有喜欢i的人
for (int k: like.get(i-1)) {
// 如果不为0,说明j可以戴i
if ((j&(1<<k)) != 0) {
// 这个时候就说明序号为k的人没有戴帽子,可以戴这顶i
// [j-(1<<k)] 表示将k这个人在方案j上的第k位的1去掉即前[i-1]个帽子不再分配给第k个人
dp[i][j] += dp[i-1][j - (1<<k)];
dp[i][j] %= N;
}
}
}
}
return dp[maxHat][total - 1];
}