我需要一些帮助来解决我已经减少到以下问题的问题。我有N个30位数,这样所有它们的组合XOR都是非零的。我需要在N个数中的每一个中添加非负(0或更多)值,使得新数的组合XOR变为0,条件是总加法值(不是加法数)最小化
例如,如果我有数字(01010) 2 ,(01011) 2 和(01100) 2 为三个数字(N = 3)。然后,它们的组合XOR是(01101) 2 。我们可以添加一些数字如下:
现在,新数字的总XOR为0,总加法数为21(= + 1 + 16 + 4)。此总增加值必须最小化(可能有更好的分布可以减少此总数,但这只是一个示例)。
这些数字各为30位,因此数字可能很大,N <= 15.如果有人能够展示一些有效的方法来解决这个问题,我将非常感激。我怀疑DP解决方案是可行的,但我无法制定任何东西。
谢谢!
答案 0 :(得分:1)
好问题:)
我想出了一种在O(n * 2 ^ n * 31 * n)中运行的方法,对于n = 15,对于一个测试用例来说有点慢(228556800)。以下是详细信息:
我在这里使用dp方法(memoization),我们将状态定义为(int mask,int pos):
掩模
0&lt; = mask&lt; 2 ^ n - 1,如果2 ^ i&amp;面具&gt; 0,我们的意思是之前添加了数字i,所有低位(&lt; = 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;
}
答案 1 :(得分:0)
算法:
找到k,给定数字的xor-sum的最高位的位置(在您的示例中为4)。确定所有给定的数字是否都设置了给定的位(如在您的示例中)。
如果他们这样做,那么你必须增加两个给定的数字,这样它们的最高位将位于位置k + 1。要确定女巫,你应该强制所有数字对并增加其中一个直到它变为2 ^(k + 1)而另一个直到xor-sum等于0.然后选择最佳对。
如果他们不这样做,那么你必须只增加一个给定数字,其第k位为0.要确定女巫,你应该强制所有这些数字并增加它们直到xor-sum等于0.然后选择最好的一个。
要确定应增加多少个数,使得xor-sum为0,计算所有其他数的xor-sum,并从中减去必须增加的数。