3

キーパッド付箋

手下たちは、ブーリアン教授の秘密の一部を安全に保管しています。またはそう彼らは考えます。実際、彼らは非常に自信を持っており、ロックのキーパッドにパスワードのヒントの付箋を貼り付けています。

ロックするには、負でない整数のペア (a、b) をキーパッドに入力する必要があります。整数は 20 億にもなる可能性があるため、付箋に助けを求めます。

付箋には2つの数字が書かれていますが、ミニオンでさえパスワードをそこに入れないように十分に知っています. 彼らは実際には、パスワード整数 (a, b) のペアの合計 (s とラベル付けされています) とビットごとの排他的論理和 (xor、x とラベル付けされています) を書き留めています。そうすれば、覚えておく必要があるのは 1 つだけです。減算が困難な場合は、ビットごとの排他的 OR を使用できます。

つまり、s = a+b および x = a^b (^ はビット単位の XOR 演算) です。

自動ハッキング装置では、推測を入力しようとするたびに数ミリ秒かかります。発見されるまでの時間はわずかであるため、すべての組み合わせを試すことができるようになるまでにかかる時間を知りたいと考えています。付箋のおかげで、特定の組み合わせをキーパッドに入力しなくても削除できるようになり、最悪の場合、ロックを解除するのにかかる時間を正確に知ることができます。

目標和と xor を持つペア (a, b) の数を見つける answer(s, x) という関数を書きます。

たとえば、s=10 で x=4 の場合、(a, b) の可能な値は (3, 7) と (7, 3) であるため、answer は 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 が大きくなりすぎると時間がかかりすぎます。このプログラムをより速く実行するにはどうすればよいですか?

4

5 に答える 5

1

これは、入力状態 (xor 桁、和桁、入力キャリー) に対して出力状態 (出力キャリー) の数が限られていることを認識することで解決できます。条件を使用して各状態に対処し、if再帰を使用して組み合わせの総数を計算できます。メモ化を使用して、再帰を効率的にすることができます。以下の私の解決策は、数値データ型の2進数字の数である問題をO(m)時間内に解決します。問題は(整数) をm指定しているため、これは技術的には解決策です。m = 32O(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;
    }
}
于 2015-05-10T01:27:16.237 に答える
0

num を HashSet に変更してみてください。最後に if/else をクリーンアップすることもできます。

例えば

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;
    }        
}
于 2015-05-04T04:02:03.150 に答える
0

アルゴリズムのほとんどのステップは、あまりにも多くの作業を実行します。

  • までのすべての非負整数に対して線形スキャンを実行しますs。問題は対称なので、 までスキャンs/2すれば十分です。
  • 2 番目の線形スキャンを実行して、を満たすa別の整数ごとに検索します。単純な代数は、そのような が 1 つしかないことを示しています。これは です。したがって、線形スキャンはまったく必要ありません。ba + b = sbs - a
  • 3 番目の線形スキャンを実行して、ペアが既に見つかっているかどうかを確認します(a, b)。only にループすると、s/2常にそれが保持されるためa ≤ b、二重カウントに苦しむことはありません。

最後に、作業を節約するための簡単な最適化を 1 つ考えてみます。

  • sが偶数の場合、abが両方とも偶数であるか、両方が奇数です。したがって、a ^ bその場合でもです。
  • sが奇数の場合、 aorbが奇数であり、したがって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;
}
于 2015-05-04T08:00:48.147 に答える