簡體   English   中英

增加一組數字,使XOR總和為0

[英]Increase set of numbers so that XOR sum is 0

我需要一些幫助來解決我已經減少到以下問題的問題。 我有N個30位數,這樣所有它們的組合XOR都是非零的。 我需要在N個數中的每一個中添加非負(0或更多)值,使得新數的組合XOR變為0,條件是總加法值(不是加法數)最小化。

例如,如果我將數字(01010) 2 ,(01011) 2和(01100) 2作為三個數字(N = 3)。 然后,它們的組合XOR是(01101) 2 我們可以添加一些數字如下:

  • (01010) 2 +(00001) 2 =(01011) 2 :(+ 1)
  • (01011) 2 +(10000) 2 =(11011) 2 :(+ 16)
  • (01100) 2 +(00100) 2 =(10000) 2 :(+ 4)

現在,新數字的總XOR為0,總加法數為21(= + 1 + 16 + 4)。 必須最小化這個總加值(可能有更好的分布,這會減少這個總數,但這只是一個例子 )。

這些數字各為30位,因此數字可能很大,N <= 15.如果有人能夠展示一些有效的方法來解決這個問題,我將非常感激。 我懷疑DP解決方案是可行的,但我無法制定任何東西。

謝謝!

好問題:)

我想出了一種在O(n * 2 ^ n * 31 * n)中運行的方法,對於n = 15,對於一個測試用例來說它有點慢(228556800)。 以下是詳細信息:

我在這里使用dp方法(memoization),我們將狀態定義為(int mask,int pos):

  • 面具

    0 <= mask <2 ^ n - 1,如果2 ^ i&mask> 0,我們的意思是之前添加了數字i,並且所有低位(<= pos)都可以被認為是零。

  • POS

    當前檢查位的位置,從高到低開始

我們從最高位到最低位開始,每次檢查當前位設置的給定數字的計數時,我們將其表示為one_cnt,如果

  • one_cnt是偶數

    當前pos的xor為零,我們只是移動到(mask,pos - 1)

  • one_cnt很奇怪

    如果one_cnt等於n(全奇數),這里我們認為是一個壞狀態並且什么都不做 否則,我們迭代在pos處包含零的數字並嘗試在此處放置一個。

注意這里當one_cnt是完全奇數時,我們認為它是壞狀態,因為我們不想增加到(pos + 1),這可能會影響先前的狀態(違反dp原則)。

但是會出現這樣的情況:arr = [1,1,1]並且存在解決方案。 所以這里我們嘗試做一些額外的計算:

我們從最高位pos開始並檢查當前位是否包含一位 ,如果是這樣,我們迭代數字以將當前位置為0的數字設置為1,然后我們開始記憶並更新結果。

例如,如果arr = [1,1,1],我們可以檢查[2,1,1],[1,2,1],[1,1,2]

希望我已經解釋得很好。

如果我想出更快的方法,我會更新解決方案:)

這是代碼:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <ctime>
#include <cassert>

using namespace std;

#define fs first
#define sc second
#define pb push_back
#define mp make_pair
#define range(i, n) for (long long i=0; i<(n); ++i)
#define forit(it,v) for(typeof((v).begin()) it = v.begin() ; it != (v).end() ; ++it)
#define eprintf(...) fprintf(stderr, __VA_ARGS__),fflush(stderr)
#define sz(a) ((int)(a).size())
#define all(a) (a).begin(),a.end()
#define two(i) (1LL<<(i))

typedef long long ll;
typedef vector<int> VI;
typedef pair<int, int> PII;

int n;
vector<ll>  arr;
ll ans;
map<PII, ll> M;

void update(ll & ret, ll tmp) {
    if (tmp == -1) return;
    if (ret == -1) ret = tmp;
    ret = min(ret, tmp);
}

/*
 * memoization(mask, pos)
 * Args:
 * mask: if 2^i in mask it means arr[i] has been added a high bit before, and all lower bit(<=pos) can be considerd zero.
 * pos: current check bit position, start from high to low
 * Return:
 *  return -1 if not valid ans exists else return minimum addition sum 
 */
int memoization(int mask, int pos) {

    if (pos < 0) {
        return 0;
    }

    PII state = mp(mask, pos);
    if (M.find(state) != M.end()) {
        return M[state];
    }

    ll &ret = M[state];
    ret = -1;

    int one_cnt = 0;
    for (int i = 0; i < n; i++) {
        if ( !(mask & two(i)) && 
                (two(pos) & arr[i])) {
            one_cnt ++;
        }
    }

    if (one_cnt % 2 == 0) { // even, xor on this pos equals zero
        ret = memoization(mask, pos - 1);
    } else {
        if (one_cnt == n)  { //full odd  bad state, do nothing
            //pass
        } else { //not full odd, choose one empty bit  to place 1  
            for (int i = 0; i < n; i++) {
                if ((mask & two(i))  //if number i has been added before, then it contain zero at pos 
                        || !(two(pos) & arr[i])  // or if number i has zero at pos and hasn't been added before
                        ) {
                    ll candi = memoization(mask | two(i), pos - 1);
                    ll added = mask & two(i) ? two(pos)  // number i has been added before, so we need extra two(pos) sum
                        //number i hasn't been added before, we need calc the new sum 
                        //here we only consider bits in [0 .. pos]
                        : two(pos) - arr[i] % two(pos + 1); 
                    if (candi >= 0)  // legal result
                        update(ret,  candi + added);
                }
            }
        }
    }

    return ret;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("g.in", "r", stdin);
#endif
    while (cin >> n) {
        arr.clear();
        for (int i = 0; i < n; i++) {
            ll val;
            cin >> val;
            arr.push_back(val);
        }

        ll max_val = arr[0];
        for (int i = 1; i < n; i++) max_val = max(max_val, arr[i]);

        int max_pos = 0;
        while (max_val) max_pos ++, max_val >>= 1;
        max_pos ++;

        //no adjust
        M.clear();
        ans = memoization(0, 31);

        bool even_bit = true;
        for (int i = max_pos; i >= 0; i--) {
            int one_cnt = 0;

            for (int j = 0; j < n; j++) one_cnt += (two(i) & arr[j]) > 0;
            even_bit &= one_cnt % 2 == 0;

            if (even_bit) {
                for (int j = 0; j < n; j++) {
                    //arr[j] at pos i is empty, try add to 1
                    if (!(two(i) & arr[j])) {
                        ll backup = arr[j];
                        arr[j] = two(i);

                        //since previous pos all contain even one bits, we just start from current pos i
                        M.clear();
                        ll candi = memoization(0, i);
                        ll added = two(i) - backup % two(i);
                        if (candi >= 0) 
                            update(ans, candi + added);

                        arr[j] = backup;
                    }
                }
            }
        }
        cout << ans << endl;
    }

    return 0;
}

算法:

找到k,給定數字的xor-sum的最高位的位置(在您的示例中為4)。 確定所有給定的數字是否都設置了給定的位(如您的示例中所示)。

如果他們這樣做,那么你必須增加兩個給定數字,這樣他們最重要的位將位於位置k + 1。 要確定女巫,你應該強制所有數字對並增加其中一個直到它變為2 ^(k + 1)而另一個直到xor-sum等於0.然后選擇最佳對。

如果他們不這樣做,那么你必須只增加一個給定數字,其第k位為0.要確定女巫,你應該強制所有這些數字並增加它們直到xor-sum等於0然后選擇最好的一個。

為了確定應該增加多少個數,使得全部的xor-sum變為0,計算所有其他數的xor-sum,並從中減去必須增加的數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM