每一位表示每个人的状态
总共有 n 个人和 40 种不同的帽子,帽子编号从 1 到 40 。
给你一个整数列表的列表 hats ,其中 hats[i] 是第 i 个人所有喜欢帽子的列表。
请你给每个人安排一顶他喜欢的帽子,确保每个人戴的帽子跟别人都不一样,并返回方案数。
由于答案可能很大,请返回它对 10^9 + 7 取余后的结果。
示例 1:
输入:hats = [[3,4],[4,5],[5]]
输出:1
解释:给定条件下只有一种方法选择帽子。
第一个人选择帽子 3,第二个人选择帽子 4,最后一个人选择帽子 5。
示例 2:
输入:hats = [[3,5,1],[3,5]]
输出:4
解释:总共有 4 种安排帽子的方法:
(3,5),(5,3),(1,3) 和 (1,5)
如果第 i顶帽子没有分配给任何人,那么会从 f[i-1][\textit{mask}]f[i−1][mask] 转移而来,即表示前 i-1i−1 顶帽子对应的分配状态就是 \textit{mask}mask,而第 ii 顶帽子不会对人的状态进行任何改变;
如果第 i顶帽子分配给了第 j个人,那么我们首先需要确定:
第 j个人是喜欢第 ii 顶帽子的;
\textit{mask}mask 的第 jj 位为 11,因为第 jj 个人被分配了第 ii 顶帽子。
class Solution {
public int numberWays(List<List<Integer>> hats) {
final int MOD = 1000000007;
int n = hats.size();
// 找到帽子编号的最大值,这样我们只需要求出 f[maxhatid][2^n - 1] 作为答案
int maxHatId = 0;
for (int i = 0; i < n; ++i) {
List<Integer> list = hats.get(i);
for (int h: list) {
maxHatId = Math.max(maxHatId, h);
}
}
// 对于每一顶帽子 h,hatToPerson[h] 中存储了喜欢这顶帽子的所有人,方便进行动态规划
List<List<Integer>> hatToPerson = new ArrayList<List<Integer>>();
for (int i = 0; i <= maxHatId; i++) {
hatToPerson.add(new ArrayList<Integer>());
}
for (int i = 0; i < n; ++i) {
List<Integer> list = hats.get(i);
for (int h: list) {
hatToPerson.get(h).add(i);
}
}
int[][] f = new int[maxHatId + 1][1 << n];
// 边界条件
f[0][0] = 1;
for (int i = 1; i <= maxHatId; ++i) {
for (int mask = 0; mask < (1 << n); ++mask) {
f[i][mask] = f[i - 1][mask];
List<Integer> list = hatToPerson.get(i);
for (int j: list) {
if ((mask & (1 << j)) != 0) {
//f[i][mask]=f[i−1][mask]+ j∈mask ∧ i∈hats[j]∑f[i−1][mask \ j]
f[i][mask] += f[i - 1][mask ^ (1 << j)];
f[i][mask] %= MOD;
}
}
}
}
return f[maxHatId][(1 << n) - 1];
}
}
在两条独立的水平线上按给定的顺序写下 nums1 和 nums2 中的整数。
现在,可以绘制一些连接两个数字 nums1[i] 和 nums2[j] 的直线,这些直线需要同时满足满足:
nums1[i] == nums2[j]
且绘制的直线不与任何其他连线(非水平线)相交。
请注意,连线即使在端点也不能相交:每个数字只能属于一条连线。
以这种方法绘制线条,并返回可以绘制的最大连线数。
示例 1:
输入:nums1 = [1,4,2], nums2 = [1,2,4]
输出:2
解释:可以画出两条不交叉的线,如上图所示。
但无法画出第三条不相交的直线,因为从 nums1[1]=4 到 nums2[2]=4 的直线将与从 nums1[2]=2 到 nums2[1]=2 的直线相交。
转为最长公共子序列
class Solution {
public int maxUncrossedLines(int[] nums1, int[] nums2) {
int m = nums1.length, n = nums2.length;
int[][] dp = new int[m + 1][n + 1];
for (int i = 1; i <= m; i++) {
int num1 = nums1[i - 1];
for (int j = 1; j <= n; j++) {
int num2 = nums2[j - 1];
if (num1 == num2) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[m][n];
}
}
Winston 构造了一个如上所示的函数 func 。他有一个整数数组 arr 和一个整数 target ,他想找到让 |func(arr, l, r) - target| 最小的 l 和 r 。
请你返回 |func(arr, l, r) - target| 的最小值。
请注意, func 的输入参数 l 和 r 需要满足 0 <= l, r < arr.length 。
class Solution {
public int closestToTarget(int[] arr, int target) {
int ans = Math.abs(arr[0] - target);
List<Integer> valid = new ArrayList<Integer>();
valid.add(arr[0]);
for (int num : arr) {
List<Integer> validNew = new ArrayList<Integer>();
validNew.add(num);
int last = num;
ans = Math.min(ans, Math.abs(num - target));
for (int prev : valid) {
int curr = prev & num;
if (curr != last) {
validNew.add(curr);
ans = Math.min(ans, Math.abs(curr - target));
last = curr;
}
}
valid = validNew;
}
return ans;
}
}
给你一个由非负整数组成的数组 nums 。另有一个查询数组 queries ,其中 queries[i] = [xi, mi] 。
第 i 个查询的答案是 xi 和任何 nums 数组中不超过 mi 的元素按位异或(XOR)得到的最大值。换句话说,答案是 max(nums[j] XOR xi) ,其中所有 j 均满足 nums[j] <= mi 。如果 nums 中的所有元素都大于 mi,最终答案就是 -1 。
返回一个整数数组 answer 作为查询的答案,其中 answer.length == queries.length 且 answer[i] 是第 i 个查询的答案。
我们可以将 \textit{nums}nums 中的每个元素看作一个长为 LL 的二进制串,将其插入字典树中。
例如 \textit{nums}=[3,10,5,25,2]nums=[3,10,5,25,2],取 L=5L=5,对应的二进制串为 [00011,01010,00101,11001,00010][00011,01010,00101,11001,00010],将其插入字典树后得到的结果如下图。
为了最大化异或值,我们可以在字典树中进行一次与检索字符串类似的过程,从根节点出发,由于异或运算具有「相同得 00,不同得 11」的性质,为了尽可能多地取到 11,我们需要在每一步寻找与当前位相反的子节点,若该节点存在则将指针移动到该节点,否则只能移动到与当前位相同的子节点。(注意由于插入和查询的二进制串长度均为 LL,非叶节点的两个子节点中,至少有一个是非空节点)
以 xi=25=(11001)2xi=25=(11001) 2
为例,下图展示了求取最大异或值的过程。