shreyk's blog

By shreyk, history, 9 months ago, In English

I recently gave a coding round of Adobe on unstop which had the following problem I can't solve: Given an array $$$x$$$ of $$$N$$$ elements and an empty array $$$y$$$. You can remove any element from $$$x$$$ and transfer it to $$$y$$$. You can also choose any element $$$a$$$ from $$$x$$$ and $$$b$$$ from $$$y$$$ and add $$$GCD(a, b)$$$ to $$$score$$$ and then you have to remove $$$a$$$ from $$$x$$$ and transfer it to $$$y$$$ while $$$b$$$ stays in $$$y$$$. You can perform both operations until $$$x$$$ is not empty. Report the maximum $$$score$$$ if you play optimally.

$$$1 \lt = N \lt = 1e4$$$
$$$1 \lt = x[i] \lt = 1e4$$$
Example
Input
N = 5
x = 2 3 4 1 6
Output
8

First delete 6 from x. Now x = [2 3 4 1], y = [6]. Select 3 and 6 then score becomes gcd(3,6) = 3 and x = [2 4 1], y = [6 3]. Choose 4 and 6, score is 3+gcd(4,6)=5, x = [2 1] y = [6 3 4]. Choose 2 and 4, score is 5+2=7. Choose 1 and 2, score is 7+1=8.

Input
N = 4
x = 1 2 3 4
Output
4
Any help is appreciated.

  • Vote: I like it
  • +4
  • Vote: I do not like it

»
9 months ago, hide # |
 
Vote: I like it +9 Vote: I do not like it

It can be solved using a Maximum Spanning Tree Approach (it works for the given test cases , atleast)

In this problem, each operation after placing the first element contributes a score equal to the gcd of the newly inserted element and some previously inserted element. With an optimal ordering, this is equivalent to choosing a spanning tree on a complete graph (with nodes representing the array elements and edge weights equal to the gcd of their values) so that the sum of the edge weights is maximized.

  • »
    »
    9 months ago, hide # ^ |
     
    Vote: I like it 0 Vote: I do not like it

    Hmm, that makes sense.
    What I tried was that for a particular value of gcd, calculate how many maximum times can I take it using seive in reverse but it didn't work.
    Thanks for the help.

  • »
    »
    9 months ago, hide # ^ |
     
    Vote: I like it 0 Vote: I do not like it

    Also, I guess the time complexity of spanning tree approach might be $$$O(N^{2})$$$ as we are traversing on a complete graph?

»
9 months ago, hide # |
 
Vote: I like it 0 Vote: I do not like it

i can think this ~~~~~ //this is code

include <bits/stdc++.h>

using namespace std;

int main(){ int n; cin>>n; vectorv(n); for(int i=0;i<n;i++){ cin>>v[i]; } sort(v.rbegin(),v.rend()); map<int,int>mp; for(int j=1;j*j<=v[0];j++){ if(v[0]%j==0){ int div1=j; int div2=v[0]/j; mp[div1]=1; mp[div2]=1; } } int ans=0; for(int i=1;i<n;i++){ int val=v[i]; int maxi=1; for(int j=1;j*j<=val;j++){ if(val%j==0){ int div1=j; int div2=val/j; if(mp.find(div1)!=mp.end()){ maxi=max(maxi,div1); } if(mp.find(div2)!=mp.end()){ maxi=max(maxi,div2); } } } ans=ans+maxi; for(int j=1;j*j<=val;j++){ if(val%j==0){ int div1=j; int div2=val/j; mp[div1]=1; mp[div2]=1; } } } cout<<ans<<endl; } ~~~~~

  • »
    »
    9 months ago, hide # ^ |
     
    Vote: I like it 0 Vote: I do not like it

    can u please tell your approach

    • »
      »
      »
      9 months ago, hide # ^ |
       
      Vote: I like it 0 Vote: I do not like it

      Yeah we should sort array in descending order as for any v[i] gcd(v[i],b) will be max for b>=v[i] so just keep track of divisors seen so for and iterate over the divisor of v[i] as gcd(v[i],b) will be some divisor of v[i] and check if that divisor is already present in array y then we can get score equal to that divisor for this v[i]

      • »
        »
        »
        »
        9 months ago, hide # ^ |
         
        Vote: I like it 0 Vote: I do not like it

        is this seems correct to you?

      • »
        »
        »
        »
        9 months ago, hide # ^ |
         
        Vote: I like it +1 Vote: I do not like it

        Test case where it will not work is quite simple. We just need to find X < Y1 < Y2 Such that gcd(x, Y2) > gcd(Y1, Y2) and gcd(X, Y1) > gcd(Y1, 2)

        We can do it X = 3*5 = 15 Y1 = 3*11 = 33 Y2 = 5 * 7 = 35 X = [15, 33, 35] Using your algorithm we get gcd(33, 35) + gcd(15, 35) = 1 + 5 = 6 but answer is gcd(15, 33) + gcd(15, 35) = 3 + 5 = 8

        • »
          »
          »
          »
          »
          9 months ago, hide # ^ |
           
          Vote: I like it 0 Vote: I do not like it

          hmm understood mistake.. thanks for the idea check my recent blog i have one more interesting problem for you on trees check that out

»
9 months ago, hide # |
 
Vote: I like it +7 Vote: I do not like it

Let make a graph.

Vertex = X[i] Vertex X[i] and X[j] have a edge if gcd(X[i], X[j]) > 1 How many edges we can have?

So, each X have O(sqrt(X)) divisors. it mean that X can be connected with no more than O(sqrt(X)) vertex -> no more than O(Nsqrt(X)) edges.

let find max spanning tree for each connectivity components and our ans is (number of connectivity components — 1) + (Sum of spanning tree weights)

  • »
    »
    9 months ago, hide # ^ |
     
    Vote: I like it 0 Vote: I do not like it

    can you see my approach above will it work?

  • »
    »
    9 months ago, hide # ^ |
     
    Vote: I like it 0 Vote: I do not like it

    Great soln. I think we can approximate no.of divisors to an upper bound of around x^(1/3).

  • »
    »
    9 months ago, hide # ^ |
     
    Vote: I like it 0 Vote: I do not like it

    how can you say that for any vertex we will not have more than O(Nsqrt(X)) edges?

    are we given the elements of X are unique ?? it can be repeating also .

    correct me if i am wrong..

    • »
      »
      »
      9 months ago, hide # ^ |
       
      Vote: I like it 0 Vote: I do not like it

      if we have X[i] == X[j] it means that we can delete X[j], add to ans X[j] and use only X[i]. After that we have all unique values.

  • »
    »
    9 months ago, hide # ^ |
     
    Vote: I like it 0 Vote: I do not like it

    Number of edges would be n*(n-1)/2 , because in worst case every vertex can have gcd > 1 for every other vertex, or we can say every vertex in connected with every other vertex. Am i missing anything?

    • »
      »
      »
      9 months ago, hide # ^ |
       
      Vote: I like it 0 Vote: I do not like it

      As I say in previous comment we can use only unique numbers X. After that from every vertex have no more than O(sqrt(X)) edges

      • »
        »
        »
        »
        9 months ago, hide # ^ |
         
        Vote: I like it 0 Vote: I do not like it

        okay now you have all even numbers & distinct, now every pair has gcd > 1 ?

        gcd(a,b) > 1 means they have a common divisor(!= 1) but for fixed gcd and fixed 'a' there can be multiple b's , isn't it?

        • »
          »
          »
          »
          »
          9 months ago, hide # ^ |
           
          Vote: I like it 0 Vote: I do not like it

          So, yes, you are right.

          In my head I have something different. I say before, that X_i and X_j have edge if gcd(X_i, X_j) > 1 but in head have that X_j is divisor of X_i. It's was a mistake.

          We add edge with X_i and all d that are divisors of X_i and if there no X_j such that X_j == d -> ans -= d

»
9 months ago, hide # |
 
Vote: I like it 0 Vote: I do not like it

how much time was given for this problem to solve in the coding round?

»
9 months ago, hide # |
 
Vote: I like it 0 Vote: I do not like it

I think you can do this greedily. What you do first is iterate through d = 1e4 to 1 (I am trying to find out the largest possible gcd with the help of d), then:
1. Initially when all elements are in left(called x in the question) and right(called y in the question) is empty, you can just try to find out if d divides any two (or more) elements in left. If yes, put them the largest in the right.
2. When there are elements on the right, find out if d divides atleast one element in the left and atleast one element in right. If yes, bring these elements in the left to right.
3. Keep repeating till you get d= 1.

As to why this is optimal, suppose instead of putting an element to the right with d'', you put it to the right later at d' (d'' > d'). Then, d'' was viable because there was an element which was also divisible by d'' in the right, and upon discovering d', this won't go away (because you are only adding elements to the right). Therefore, d'' will always be an option, even when dividing by d' and it would make sense to choose d' when d'' is an option.
If there is any error in my approach or some confusion, please tell me (along with my mistake in my proof I am not really good at proofs)!

»
9 months ago, hide # |
Rev. 3  
Vote: I like it 0 Vote: I do not like it

for numbers <= 1e4 there can be atmost (25-50) divisors (lets take it 100)

now sort array in reverse order

now iterate from largest element to smallest

for current element iterate over its divisors (from largest to smallest) and check if any of its multiple has been added to y (easily maintainable using a hashmap)

add it to ans move the current element to array y and increase count for its divisors.

O(100*n*logn) over all test cases

isn't this greedy correct?

  • »
    »
    9 months ago, hide # ^ |
     
    Vote: I like it 0 Vote: I do not like it

    Bad testcase:

    35 33 15

    Your algorithm by iterations:

    X = 35, 33, 15 Y = []

    take 35

    Y = [35, 7, 5, 1]

    take 33

    33 = 11 * 3, no one of it is in Y -> +1

    Y = [35, 33, 11, 7, 5, 3, 1]

    take 15

    15 = 3* 5, 5 is in Y -> take 5.

    ans = 6

    best ans is 8

    35 & 15 ( + 5)

    15 & 33 ( + 3)