3

Problem statement:

Given an array of n elements and an integer k, find an integer x in the range [0,k] such that Xor-sum(x) is maximized. Print the maximum value of the equation.

Xor-sum(x)=(x XOR A1)+(x XOR A[2])+(x XOR A[3])+…………..+(x XOR A[N])

Input Format

The first line contains integer N denoting the number of elements in A. The next line contains an integer, k, denoting the maximum value of x. Each line i of the N subsequent lines(where 0<=i<=N) contains an integer describing Ai.

Constraints

1<=n<=10^5
0<=k<=10^9
0<=A[i]<=10^9

Sample Input

3
7
1
6
3

Sample Output

14

Explanation

Xor_sum(4)=(4^1)+(4^6)+(4^3)=14.

This problem was asked in Infosys requirement test. I was going through previous year papers & I came across this problem. I was only able to come up with a brute-force solution which is just to calculate the equation for every x in the range [0,k] and print the maximum. But, the solution won't work for the given constraints.

My solution

#include <bits/stdc++.h>
using namespace std;
int main()
{
  int n, k, ans = 0;
  cin >> n >> k;
  vector<int> a(n);
  for (int i = 0; i < n; i++) cin >> a[i];
  for (int i = 0; i <= k; i++) {
    int temp = 0;
    for (int j = 0; j < n; j++) {
      temp += (i ^ a[j]);
    }
    ans = max(temp, ans);
  }
  cout << ans;
  return 0;
}

I found the solution on a website. I was unable to understand what the code does but, this solution gives incorrect answer for some test cases.

Scroll down to question 3

infernus-85
  • 435
  • 5
  • 12
  • 5
    [Why should I not `#include `?](https://stackoverflow.com/questions/31816095/why-should-i-not-include-bits-stdc-h) [Why is `using namespace std;` considered bad practice?](https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice) – Evg Jan 07 '22 at 14:11
  • 2
    1(001), 6(110), and 3(011) have a majority of zeros in their most significant bit (within the max limit), so that bit position in `x` should be on to turn on the majority in the result. Do the same analysis for each bit. – 500 - Internal Server Error Jan 07 '22 at 14:38
  • @Evg: I do agree with the first point about the include; however, the second point, I think it is a totally acceptable style to use a `using namespace std` within a source file (provided it doesn't contravene some style guide you should be following) as it will only create ambiguity in the one file you are working with. This of course assumes you have sole dictatorship/ownership over this source file. As such I think you should qualify your second point as a `nitpick`. – ldog Jan 09 '22 at 08:12
  • 1
    @ldog This is *far* from being a nitpick. Most beginners don't understand what `using namespace X;` implies, they just silently follow code examples found on numerous garbage websites. As a result, they pick bad habits without realizing that they are bad and knowing why they are bad. It's much better to start from proper training than to retrain people later. `using namespace X;` declaration is dangerous. It has valid use cases, but blind copy-pasting is not one of them. Take a look at [this question](https://stackoverflow.com/questions/20554231/why-is-this-swap-function-call-ambiguous). – Evg Jan 09 '22 at 08:37

5 Answers5

2

The trick here is that XOR works on bits in parallel, independently. You can optimize each bit of x. Brute-forcing this takes 2*32 tries, given the constraints.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • Can you please elaborate or give an example? How do I optimize each bit of x? Also, how do I make sure that x is always less than k? – infernus-85 Jan 07 '22 at 14:32
  • 1
    @infernus-85: Pretty much all the same answer. You start with the MSB and for each bit, check if that bit must be 1. One of the checks will be if adding `1< – MSalters Jan 07 '22 at 14:45
  • So, you would start form the msb (let's say 32) and check 32th bit of every number. Then if you find more zeros than ones, then x's msb will be one to filp the original bits. Then, do this for every bit. Is that what you're saying? – infernus-85 Jan 07 '22 at 14:59
  • The problem statement does not restrict `k` to the form "2^a-1". This complicates the solution. – nielsen Jan 07 '22 at 15:07
  • @nielsen: Marginally. The difference is between a `break` and a `continue`. – MSalters Jan 07 '22 at 17:03
  • @Msalters So, you brute force for eat bit of x and keep checking if x exceeds k at any point. This would take O(2*32*10^5) since n can be at most 10^5. Right? – infernus-85 Jan 08 '22 at 02:04
  • @infernus-85: Well, O(2*32*10^5) is the same as O(1), so theoretically you're right. But `A[ ]` could be preprocessed to reduce the runtime further. – MSalters Jan 08 '22 at 13:32
2

As said in other comments each bit of x will give an independent contribution to the sum, so the first step is to calculate the added value for each possible bit.

To do this for the i-th bit of x count the number of 0s and 1s in the same position of each number in the array, if the difference N0 - N1 is positive then the added value is also positive and equal to (N0-N1) * 2^i, let's call such bits "useful".

The number x will be a combination of useful bits only.

Since k is not in the form 2^n - 1, we need a strategy to find the best combination (if you don't want to use brute force on the k possible values).

Consider then the binary representation of k and loop over its bits starting from the MSB, initializing two variables: CAV (current added value) = 0, BAV (best added value) = 0.

If the current bit is 0 loop over.

If the current bit is 1:

a) calculate the AV sum of all useful bits with lower index plus the CAV, if the result is greater then the BAV then replace BAV

b) if the current bit is not useful quit loop

c) add the current bit added value to CAV

When the loop is over, if CAV is greater than BAV replace BAV

EDIT: A sample implementation (in Java, sorry :) )

public class XorSum {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int k=sc.nextInt();
        int[] a=new int[n];
        for (int i=0;i<n;i++) {
            a[i]=sc.nextInt();
        }

        //Determine the number of bits to represent k (position of most significant 1 + 1)
        int msb=0;
        for (int kcopy=k; kcopy!=0; kcopy=kcopy>>>1) {
            msb++;
        }
        
        
        //Compute the added value of each possible bit in x
        int[] av=new int[msb];
        int bmask=1;
        for (int bit=0;bit<msb;bit++) {
            int count0=0;
            for (int i=0;i<n;i++) {
                if ((a[i]&bmask)==0) {
                    count0++;
                }
            }
            av[bit]=(count0*2-n)*bmask;
            bmask = bmask << 1;
        }
        
        //Accumulated added value, the value of all positive av bits up to the index
        int[] aav=new int[msb];
        for (int bit=0;bit<msb;bit++) {
            if (av[bit]>0) {
                aav[bit]=av[bit];
            }
            if (bit>0) {
                aav[bit]+=aav[bit-1];
            }
        }


        //Explore the space of possible combinations moving on the k boundary
        int cval=0;
        int bval=0;

        bmask = bmask >>> 1;
        //Start from the msb
        for (int bit=msb-1;bit>=0;bit--) {
            //Exploring the space of bit combination we have 3 possible cases:
            //bit of k is 0, then we must choose 0 as well, setting it to 1 will get x to be greater than k, so in this case just loop over
            if ((k&bmask)==0) {
                continue;
            }
            //bit of k is 1, we can choose between 0 and 1:
            //- choosing 0, we can immediately explore the complete branch considering that all following bits can be set to 1, so just set to 1 all bits with positive av
            //  and get the meximum possible value for this branch
            int val=cval+(bit>0?aav[bit]:0);
            if (val>bval) {
                bval=val;
            }
            //- choosing 1, if the bit has no positive av, then it's forced to 0 and the solution is found on the other branch, so we can stop here
            if (av[bit]<=0) break;
            //- choosing 1, with a positive av, then store the value and go on with this branch
            cval+=av[bit];
        }
        if (cval>bval) {
            bval=cval;
        }
        
        //Final sum
        for (int i=0;i<n;i++) {
            bval+=a[i];
        }
        
        System.out.println(bval);
        
    }
}
Rocco
  • 1,098
  • 5
  • 11
  • Sorry, I am a bit confused. So, if N0-N1 is positive for the i'th bit, we turn on i'th bit of x. We do this for every bit. Then, chances are that the 'x' built will be greater than 'k'. To make it <=k we start form the msb of x and turn bits off from left to right until x<=k. Am I right? – infernus-85 Jan 07 '22 at 16:26
  • @infernus-85 exactly, we start from the maximum possible value of x (that is k), then try turning off bits one by one, when a bit is turned off automatically all lower bits become possible at the same time, so we include all of them without further checking the permutations – Rocco Jan 07 '22 at 16:34
  • @infernus-85 I wrote exactly, but I actually meant "not exactly", sorry – Rocco Jan 07 '22 at 16:45
  • what do you mean? – infernus-85 Jan 07 '22 at 16:48
  • @infernus-85 you wrote "we start from msb and turn bits off until x<=k", actually we always keep x <=k, never exploring higher values – Rocco Jan 07 '22 at 16:54
  • but doesn't it work either way? You would still get the same x. But, I guess keeping x always less than k is more efficient. – infernus-85 Jan 07 '22 at 17:00
  • @infernus-85 I'm not sure about your approach, please elaborate, consider that in the proposed solution only one bit at a time is turned off, if you start with all useful bits set to 1 i'ts possible that turning on just one bit will not give a valid x value – Rocco Jan 07 '22 at 17:07
  • We build a 32-bit integer x without considering k. So, we build an x that is unbounded. For every i'th bit of n elements of the array if N0-N1>0 , we set the i'th bit of x to 1 else to 0. After all 32 bits we'll have a 32-bit unbounded x. Since all the bits of x are independently contributing to the answer, we can now bound x by making it less than or equal to k. For this, we start at the 32'th bit of x and keep turning the bits off from left to right until we get x<=k. Again, this is possible because of every bit of x contributes to the answer independently. – infernus-85 Jan 07 '22 at 17:47
  • @infernus-85 this could not work, consider for instance: bit 0 1 2 3 all useful, K = 12, added value for bit 3 is 64 and added value for all other bits is 16, you start with 1111 (15 dec), then turn off bit 3 and get 0111 that has a smaller value than 1000 (48 vs 64) – Rocco Jan 07 '22 at 18:00
  • I see the problem now. So, your approach is to start at the msb of k and then build x. – infernus-85 Jan 07 '22 at 18:11
  • @yes, in the example above I would start with x = 1100 (12), revert bit 3 and check branch where bit 3 is 0 and all others 1, that is 0111 this has the overall value of 48, so it's temporarily the best, store bit 3 value on CAV (64), then go on with bit 2, turn off and check branch 1011 value 96, again the best value, update CAV with bit 2, now is 80, finally check CAV = 80 against BAV = 96, BAV wins so the best mix is 1011 – Rocco Jan 07 '22 at 18:21
  • I think this solution is wrong. Consider k=6, n=1 and A[0]=2. The correct answer is x=5, xorsum(5)=5^2=7. But the LSB in k is not set, yet it is set in x=5. – MSalters Jan 08 '22 at 00:26
  • @MSalters in this case in the first step you will determine that the useful bits are 0 an 2 (101), then start with x = 6 (110), the loop will produce (in sequence), 011, 101, 110 so the solution would be found – Rocco Jan 09 '22 at 07:20
  • @MSalters but I added a sample implementation, so maybe you can check it if you want – Rocco Jan 09 '22 at 16:22
0

I think you can consider solving for each bit. The number X should be the one that can turn on many high-order bits in the array. So you can count the number of bits 1 for 2^0, 2^1, ... And for each position in the 32 bits consider turning on the ones that many number has that position to be bit 0.

Combining this with the limit K should give you an answer that runs in O(log K) time.

0

Assuming k is unbounded, this problem is trivial.

For each bit (assuming 64-bit words there would be 64 for example) accumulate the total count of 1's and 0's in all values in the array (for that bit), with c1_i and c0_i representing the former and latter respectively for bit i.

Then define each bit b_i in x as

x_i = 1 if c0_i > c1_i else 0

Constructing x as described above is guaranteed to give you the value of x that maximizes the sum of interest.


When k is specific number, this can be solved using a dynamic programming solution. To understand how, first derive a recurrence.

Let z_0,z_1,...,z_n be the positions of ones occuring in k's binary representation with z_0 being the most significant position.

Let M[t] represent the maximum sum possible given the problem's array and defining any x such that x < t.

Important note: the optimal value of M[t] for t a power of 2 is obtained by following the procedure described above for an unbounded k, but limiting the largest bit used.

To solve this problem, we want to find

M[k] = max(M[2^z_0],M[k - 2^z_0] + C_0)

where C_i is defined to be the contribution to the final sum by setting the position z_i to one.

This of course continues as a recursion, with the next step being:

M[k - 2^z_0] = max(M[2^z_1],M[k - 2^z_0 - 2^z_1] + C_1)

and so on and so forth. The dynamic programming solution arises by converting this recursion to the appropriate DP algorithm.

Note, that due to the definition of M[k], it is still necessary to check if the sum of x=k is greater than M[k], as it may still be so, but this requires one pass.

ldog
  • 11,707
  • 10
  • 54
  • 70
  • Sorry, I tried but I can't understand this solution. I'm not familiar with dp, I'm still trying to learn it. If you could please write the code for this algorithm, I'll mark this as correct and it would also help me understand. Thanks. – infernus-85 Jan 10 '22 at 03:57
  • @infernus-85: it would appear the link you included with the original question states that the expected format of the exam (I don't know which but apparently you are training for one?) has questions where "One is of Hard difficulty level, and usually based on Dynamic Programming." If you don't understand DP it appears you could never solve the hard question. – ldog Jan 10 '22 at 05:15
  • @Idog The exam requires you to solve 2 out of 3 problems to get the role. But, I am not a complete beginner in dp. I have solved all the dp problems on cses, some on leetcode, some in tests. I can solve variations of the problems I've seen before. A completely new dp problem is where I struggle. I have also solved digit dp problems, but I've never encountered a dp problem where you have to deal with bits. – infernus-85 Jan 10 '22 at 05:19
0

At bit level it is simple 0 XOR 0, 1 XOR 1 = 0 and last one 0 XOR 1 = 1, but when these bit belongs to a number XOR operations have addition and subtraction effect. For example if third bit of a number is set and num XOR with 4 (0100) which also have third bit set then result would be subtraction from number by 2^(3-1), for example num = 5 then 0101 XOR 0100 = 0001, 4 subtracted in 5 , Similarly if third bit of a number is not set and num XOR with 4 then result would be addition for example num = 2 then 0010 XOR 0100 = 0101, 4 will be added in 2. Now let’s see this problem,

This problem can’t be solved by applying XOR on each number individually, rather the approach to solve this problem is Perform XOR on particular bit of all numbers, in one go! . Let’s see how it can be done?

Fact 1: Let’s consider we have X and we want to perform XOR on all numbers with X and if we know second bit of X is set, now suppose somehow we also know that how many numbers in all numbers have second bit set then we know answer 1 XOR 1 = 0 and we don’t have to perform XOR on each number individually.

Fact 2: From fact 1, we know how many numbers have a particular bit set, let’s call it M and if X also have that particular bit set then M * 2^(pos -1) will be subtracted from sum of all numbers. If N is total element in array than N - M numbers don’t have that particular bit set and due to it (N – M) * 2^(pos-1) will be added in sum of all numbers.

From Fact 1 and Fact 2 we can calculate overall XOR effect on a particular bit on all Numbers by effect = (N – M)* 2^(pos -1) – (M * 2^(pos -1)) and can perform the same for all bits. Now it’s time to see above theory in action, if we have array = {1, 6, 3}, k = 7 then,

1 = 0001 (There are total 32 bits but I am showing only relevant bits other bits are zero) 
6 = 0110
3 = 0011

So our bit count list = [0, 1, 2, 2] as you can see 1 and 3 have first bit set, 6 and 3 have second bit set and only 6 have third bit set.

X = 0, …, 7 but X = 0 have effect = 0 on sum because if bit is not set then it doesn’t not affect other bit in XOR operation, so let’s star from X = 1 which is 0001,

[0, 1, 2, 2] = count list,
[0, 0, 0, 1] = X

As it is visible in count list two numbers have first bit set and X also have first bit set, it means 2 * 2^(1 – 1) will be subtract in sum and total numbers in array are three, so (3 – 2) * 2^(1-1) will be added in sum. Conclusion is XOR of first bit is, effect = (3 – 2) * 2^(1-1) - 2 * 2^(1 – 1) = 1 – 2 = -1. It is also overall effect by X = 1 because it only has first bit set and rest of bits are zero. At this point we compare effect produced by X = 1 with X = 0 and -1 < 0 which means X = 1 will reduce sum of all numbers by -1 but X = 0 will not deduce sum of all numbers. So until now X = 0 will produce max sum.

The way XOR is performed for X = 1 can be performed for all other values and I would like to jump directly to X = 4 which is 0100

[0, 1, 2, 2] = count list,
[0, 1, 0, 0] = X

As it is visible X have only third bit set and only one number in array have first bit set, it means 1 * 2^(3 – 1 ) will be subtracted and (3 – 1) * 2^(3-1) will be added and overall effect = (3 – 1) * 2^(3-1) - 1 * 2^(3 – 1 ) = 8 – 4 = 4. At this point we compare effect of X = 4 with known max effect which is effect = 0 so 4 > 0 and due to this X = 4 will produce max sum and we considered it. When you perform this for all X = 0,…,7, you will find X = 4 will produce max effect on sum, so the answer is X = 4.

So

(x XOR arr[0]) + ( x XOR arr[1]) +….. + (x XOR arr[n]) = effect + sum(arr[0] + sum[1]+ …. + arr[n])

Complexity is, O(32 n) to find for all 32 bits, how many number have a particular bit set, plus,
O(32 k) to find effect of all X in [0, k],
Complexity = O(32 n) + O(32 k) = O(c n) + O(c k), here c is constant, finally
Complexity = O(n)

#include <iostream>

#include <cmath>
#include <bitset>

#include <vector>
#include <numeric>

std::vector<std::uint32_t> bitCount(const std::vector<std::uint32_t>& numList){

    std::vector<std::uint32_t> countList(32, 0);

    for(std::uint32_t num : numList){

        std::bitset<32> bitList(num);

        for(unsigned i = 0; i< 32; ++i){

            if(bitList[i]){

                countList[i] += 1;
            }
        }
    }

    return countList;
}

std::pair<std::uint32_t, std::int64_t> prefXAndMaxEffect(std::uint32_t n, std::uint32_t k,
                                                 const std::vector<std::uint32_t>& bitCountList){

    std::uint32_t prefX = 0;
    std::int64_t xorMaxEffect = 0;

    std::vector<std::int64_t> xorBitEffect(32, 0);

    for(std::uint32_t x = 1; x<=k; ++x){

        std::bitset<32> xBitList(x);

        std::int64_t xorEffect = 0;

        for(unsigned i = 0; i< 32; ++i){

            if(xBitList[i]){

                if(0 != xorBitEffect[i]){

                    xorEffect += xorBitEffect[i];
                }
                else{

                    std::int64_t num = std::exp2(i);

                    xorBitEffect[i] = (n - bitCountList[i])* num - (bitCountList[i] * num);

                    xorEffect += xorBitEffect[i];
                }
            }
        }

        if(xorEffect > xorMaxEffect){

            prefX = x;
            xorMaxEffect = xorEffect;
        }
    }

    return {prefX, xorMaxEffect};
}

int main(int , char *[]){

    std::uint32_t k = 7;
    std::vector<std::uint32_t> numList{1, 6, 3};

    std::pair<std::uint32_t, std::int64_t> xAndEffect = prefXAndMaxEffect(numList.size(), k, bitCount(numList));

    std::int64_t sum = 0;
    sum = std::accumulate(numList.cbegin(), numList.cend(), sum) + xAndEffect.second;

    std::cout<< sum<< '\n';
}

Output :
14

Vikas Awadhiya
  • 290
  • 1
  • 8