仆从们布鲁恩教授的一些秘密安全地被锁定了。或者他们认为。事实上,他们是如此自信,他们甚至有一个密码暗示粘滞在键盘上的粘滞便笺。
锁要求您在键盘中输入一对非负整数(a,b)。由于整数可能高达20亿,所以你可以在粘滞便笺中寻求帮助。
粘滞便笺上写着两个数字,但即便是仆从也知道不要把密码放在那里。他们实际上已经将这些总和(它们标记为s)和一对密码整数(a,b)的按位异或(xor,标记为x)写下来。这样,他们只需要记住一个。如果他们在减法方面有困难,他们可以使用按位异或。
即,我们有s = a + b和x = a ^ b(其中^是按位XOR运算)。
使用自动黑客设备,每次输入猜测的尝试都需要几毫秒。由于您在发现之前只有一点时间,因此您想知道在尝试所有组合之前可能需要多长时间。由于粘滞便笺,您现在可以消除某些组合,甚至无需将它们输入键盘,您可以确切地知道破解锁定所需的时间 - 在最坏的情况下。
编写一个名为answer(s,x)的函数,该函数查找具有目标和和xor的对(a,b)的数量。
例如,如果s = 10且x = 4,则(a,b)的可能值为(3,7)和(7,3), 所以答案会回归2。
如果s = 5且x = 3,则没有可能的值,因此answer将返回0.
s和x至少为0且最多为20亿。
要提供Python解决方案,请编辑solution.py 要提供Java解决方案,请编辑solution.java
输入: (int)s = 10 (int)x = 4 输出: (int)2
输入: (int)s = 0 (int)x = 0 输出: (int)1
public static int answer(int s, int x) {
List<Integer> num = new ArrayList<>();
int a;
int b;
int sum;
int finalans;
for(int i = 0; i <=s; i++){
for(int e = 0; e <= s; e++){
sum = i + e;
if(sum == s){
if((i^e) == x){
if(!num.contains(i)){
num.add(i);
}
if(!num.contains(e)){
num.add(e);
}
}
}
}
}
finalans = num.size();
if((finalans%2) == 0){
return finalans*2;
} else if(!((finalans%2) == 0)){
return finalans;
}
return 0;
}
我的代码有效,但是当s和x变得太大时,它需要很长时间。如何让这个程序更快地运行?
答案 0 :(得分:1)
您可以通过实现对于传入状态(xor数字,总和数字,传入进位)来解决这个问题,因此存在有限数量的传出状态(传出进位)。您可以使用if
条件处理每个状态,并使用递归来计算组合的总数。您可以使用memoization使递归有效。我的解决方案解决了O(m)
时间内的问题,其中m
是数字数据类型中的二进制数字的数量。由于问题指定了m = 32
(整数),因此这在技术上是O(1)
解决方案。
如果您有任何疑问,请与我们联系。我试图在代码中添加有用的注释来解释各种情况。
public class SumAndXor {
public static void main(String[] args) {
int a = 3;
int b = 7;
int sum = a + b;
int xor = a ^ b;
System.out.println(answer(sum, xor));
}
private static final int NOT_SET = -1;
// Driver
public static int answer(int sum, int xor) {
int numBitsPerInt = Integer.toBinaryString(Integer.MAX_VALUE).length() + 1;
int[][] cache = new int[numBitsPerInt][2];
for (int i = 0; i < numBitsPerInt; ++i) {
cache[i][0] = NOT_SET;
cache[i][1] = NOT_SET;
}
return answer(sum, xor, 0, 0, cache);
}
// Recursive helper
public static int answer(int sum, int xor, int carry, int index, int[][] cache) {
// Return memoized value if available
if (cache[index][carry] != NOT_SET) {
return cache[index][carry];
}
// Base case: nothing else to process
if ((sum >> index) == 0 && (xor >> index) == 0 && carry == 0) {
return 1;
}
// Get least significant bits
int sumLSB = (sum >> index) & 1;
int xorLSB = (xor >> index) & 1;
// Recursion
int result = 0;
if (carry == 0) {
if (xorLSB == 0 && sumLSB == 0) {
// Since the XOR is 0, the binary digits are either [0, 0] or [1, 1]. Since the
// sum is 0 and the incoming carry is 0, both [0, 0] and [1, 1] are valid. We
// recurse with a carry of 0 to represent [0, 0], and we recurse with a carry of
// 1 to represent [1, 1].
result = answer(sum, xor, 0, index + 1, cache) + answer(sum, xor, 1, index + 1, cache);
} else if (xorLSB == 0 && sumLSB == 1) {
// Since the XOR is 0, the binary digits are either [0, 0] or [1, 1]. Since the
// sum is 1 and the incoming carry is 0, neither [0, 0] nor [1, 1] is valid.
result = 0;
} else if (xorLSB == 1 && sumLSB == 0) {
// Since the XOR is 1, the binary digits are either [0, 1] or [1, 0]. Since the
// sum is 0 and the incoming carry is 0, neither [0, 1] nor [1, 0] is valid.
result = 0;
} else if (xorLSB == 1 && sumLSB == 1) {
// Since the XOR is 1, the binary digits are either [0, 1] or [1, 0]. Since the
// sum is 1 and the incoming carry is 0, both [0, 1] and [1, 0] is valid. We
// recurse with a carry of 0 to represent [0, 1], and we recurse with a carry
// of 0 to represent [1, 0].
result = 2 * answer(sum, xor, 0, index + 1, cache);
}
} else {
if (xorLSB == 0 && sumLSB == 0) {
// Since the XOR is 0, the binary digits are either [0, 0] or [1, 1]. Since the
// sum is 0 and the incoming carry is 1, neither [0, 0] nor [1, 1] is valid.
result = 0;
} else if (xorLSB == 0 && sumLSB == 1) {
// Since the XOR is 0, the binary digits are either [0, 0] or [1, 1]. Since the
// sum is 1 and the incoming carry is 1, both [0, 0] and [1, 1] are valid. We
// recurse with a carry of 0 to represent [0, 0], and we recurse with a carry of
// 1 to represent [1, 1].
result = answer(sum, xor, 0, index + 1, cache) + answer(sum, xor, 1, index + 1, cache);
} else if (xorLSB == 1 && sumLSB == 0) {
// Since the XOR is 1, the binary digits are either [0, 1] or [1, 0]. Since the
// sum is 0 and the incoming carry is 1, both [0, 1] and [1, 0] are valid. We
// recurse with a carry of 0 to represent [0, 1], and we recurse with a carry
// of 0 to represent [1, 0].
result = 2 * answer(sum, xor, 1, index + 1, cache);
} else if (xorLSB == 1 && sumLSB == 1) {
// Since the XOR is 1, the binary digits are either [0, 1] or [1, 0]. Since the
// sum is 1 and the incoming carry is 1, neither [0, 1] nor [1, 0] is valid.
result = 0;
}
}
cache[index][carry] = result;
return result;
}
}
答案 1 :(得分:1)
无论如何,在2D图形或Excel电子表格中使用S表示行,x表示列(将零留空)有助于绘制正确答案。然后,寻找模式。数据点实际上形成了一个Sierpinski三角形(见http://en.wikipedia.org/wiki/Sierpinski_triangle)。
您还会注意到,列中的每个数据点(大于零)对于该列中的所有实例都是相同的,因此,给定您的x值,您将自动知道最终答案应该是什么,只要与S值对应的行与三角形中的数据点相交。您只需要确定S值(行)是否与列x处的三角形相交。有意义吗?
即使列中的值形成从0到x的模式:1,2,2,4,2,4,4,8,2,4,4,8,4,8,8,16 ..我相信你能搞清楚。
这是&#34;给出的最终值x&#34;方法以及大多数剩余的代码(在Python中...... Java太冗长和复杂)。你只需要编写三角形遍历算法(我不会放弃它,但这是朝着正确方向的坚实推动):
def final(x, t):
if x > 0:
if x % 2: # x is odd
return final(x / 2, t * 2)
else: # x is even
return final(x / 2, t)
else:
return t
def mid(l, r):
return (l + r) / 2
def sierpinski_traverse(s_mod_xms, x. lo, hi, e, l, r):
# you can do this in 16 lines of code to end with...
if intersect:
# always start with a t-value of 1 when first calling final in case x=0
return final(x, 1)
else:
return 0
def answer(s, x):
print final(x, 1)
if s < 0 or x < 0 or s > 2000000000 or x > 2000000000 or s < x or s % 2 != x % 2:
return 0
if x == 0:
return 1
x_modulus_size = 2 ** int(math.log(x, 2) + 2)
s_mod_xms = s % x_modulus_size
lo_root = x_modulus_size / 4
hi_root = x_modulus_size / 2
exp = x_modulus_size / 4 # exponent of 2 (e.g. 2 ** exp)
return sierpinski_traverse(s_mod_xms, x, lo_root, hi_root, exp, exp, 2 * exp)
if __name__ == '__main__':
answer(10, 4)
答案 2 :(得分:0)
尝试将num更改为HashSet。你也可以在最后清理你的if / else。
e.g。
public static int answer(int s, int x) {
HashSet<Integer> num = new HashSet<>();
int a;
int b;
int sum;
int finalans;
for(int i = 0; i <=s; i++){
for(int e = 0; e <= s; e++){
sum = i + e;
if(sum == s){
if((i^e) == x){
num.add(i);
num.add(e);
}
}
}
}
finalans = num.size();
if((finalans%2) == 0){
return finalans*2;
} else {
return finalans;
}
}
答案 3 :(得分:0)
算法中的大多数步骤都会执行太多工作:
s
。由于问题是对称的,因此扫描到s/2
就足够了。a
的每个b
另一个整数a + b = s
。简单代数表明只有一个b
,s - a
,因此根本不需要线性扫描。(a, b)
。如果您只循环到s/2
,它将始终保持a ≤ b
,因此您不会遭受重复计算。最后,我可以想到一个简单的优化来节省一些工作:
s
是偶数,那么a
和b
都是偶数,或者都是奇数。因此,a ^ b
就是这种情况。s
为奇数,则a
或b
为奇数,因此a ^ b
为奇数。您可以在执行任何工作之前添加该检查:
public static int answer(int s, int x) {
int result = 0;
if (s % 2 == x % 2) {
for (int a = 0; a <= s / 2; a++) {
int b = s - a;
if ((a ^ b) == x) {
result += 2;
}
}
// we might have double counted the pair (s/2, s/2)
// decrement the count if needed
if (s % 2 == 0 && ((s / 2) ^ (s / 2)) == x) {
result--;
}
}
return result;
}
答案 4 :(得分:0)
进一步解释我之前的答案,从字面上看大局。三角形遍历算法的工作方式类似于二分搜索,除了三个选项而不是两个(“三元搜索”?)。查看包含S和x的最大三角形内的3个最大三角形。然后,选择那些包含S和x的三角形。然后,查看新选择的三角形中的三个最大三角形,然后选择包含S和x的三角形。重复,直到你到达一个点。如果该点不为零,则返回我指定的“最终”值。如果你选择一个三角形并且行S不与数据点相交,那么有一些if-else语句也会加快这一点。