Google Interview Question
Software Engineer / DevelopersCountry: United States
Interview Type: Phone Interview
this makes sense. I wrote some code to elaborate the idea. let me know if I get it right. time complexcity O(n^3)
assume input as two arrays: list[N] has N integers, operator[N] has N corresponding operators
int dp[][]=new int[N][N]; /* row i represents computing with starting point of list[i] and column j represents the max result with j numbers after list[i];*/
int max=Integer.MIN_VALUE;
for(int i=0;i<N;i++){
dp[i][0]=list[i];}
for(int j=1;j<N;j++){
for(int i=0;i<N;i++){
for(int k=0;k<j;k++){
int val=compute(dp[i][k],operator[(i+k)%N],dp[(i+k+1)%N][j-k-1]);
if(val>dp[i][j]) dp[i][j]=val;
if(val>max&&j==N-1) max=val;
}}}
return max;
I think this might have a problem since the the integers are allowed to be negative:
since it is not true that max{m[i,k] [operation m[k+1,j]} is always the right thing to look at, since if m[i,k] is negative, we want m[k+1,j] to be as small as possible...
It reminds me matrix-chain-multiplication but with start point at any vertex. Complexity of matrix-chain-multiplication is O(n^3) so the complexity of this algorithm is O(n^4). Here is algorithm:
int result(int[] vertices, Op[] edges) {
int [][] dp = null;// initialize with -Integer.min_value and i,i with vertices [i]
int n = vertices.length;
int result = Integer.MIN_VALUE;
for (int startVertex = 0; startVertex < n; startVertex++) {
for (int length = 2; length <= n; length++) {
for (int first = startVertex; first <= n - length + startVertex; first++) {
int last = first + length - 1;
int max = Integer.MIN_VALUE;
for (int divider = first; divider < last; divider++) {
max = Math.max(max, edges[first % n].exec(dp[first % n][divider % n], dp[(divider + 1) % n][last % n]));
}
dp[first][last] = max;
}
}
result = Math.max(result, dp[startVertex][(startVertex + n) % n]);
}
return result;
}
Assuming this would be kept in a circular linked list, traverse it once and collapse vertices as you see addition operation. After the first pass you will be left with only multiplication edges. In the second pass do the multiplications and the result is what you are looking for.
seems like a NP problem. Can be solved using recusrsion:
Repeat for each edge: collapse edge, call recursively, restore edge.
The answer is the maximum value returned from all the recursive calls in the loop above.
Complexity: O(N!) Space: O(N)
The ending condition for the recursion is what's specified in the question (when there are two edges left).
A circular variant of matrix chain multiplication algorithm can be used. In the original solution, in the N*N array (say A), only cells with i <= j are filled, but here we would like to fill cells for which i>j as well.
Recursive definition:
V[0...N-1] - list of values on the vertices.
O[0...N-1] - list of operators.. Oi is the operator on edge connecting vertices i and i+1.
A[i, j] = Vi if i == j
= max { Ok(A[i,k], A[k,j] } for k: i->j if i<j
for k:i->N-1 and k:0->j if j<i
Example: V[-2, 3, 2, 4, -8, 5], O[*, +, *, *, +, *]
A:
iter1
-2 0 0 0 0 0
0 3 0 0 0 0
0 0 2 0 0 0
0 0 0 4 0 0
0 0 0 0 -8 0
0 0 0 0 0 5
iter2:
-2 0 0 0 0 -10
-6 3 0 0 0 0
0 5 2 0 0 0
0 0 8 4 0 0
0 0 0 -32 -8 0
0 0 0 0 -3 5
and so on to fill the whole table. And the maximum element in the filled array is the answer.
O(n*n) time+ O(n*n) space.
I think matrix chain multiplication algorithm can be applied in this problem. Comparing to classical dynamic program method, what we need to do here is to store two values for a random combination[i, j]: the positive number with largest abstract value; the negative number with largest abstract value.(Since we need to handle * and + operations)
is there any operator presidency means to say we can go sequential or first multiplication then addition ..
dynamic programming will give you the solution in O(n*n) + O(n*n) complexity
Here is solution :
lets say you have hexagon( with following 1(15)-2(9)-3(12)-4(1)-5(7)-6(12)-1(15)), where values written in *()* is the value at each node
Now make a 2d matrix of size 6x6, write these values in the main diagonal,
15.24.36..37.44.56
.......9...21.22.29.41
............12.13.20.32
....................1..8..20
........................7..19
.............................12
the right most corner value is the answer
ok, I have taken + as operator but because i have used dynamic programming here, so the operator does not matter, i will adjust accordingly.
can you elaborate with some code? I can't get it how to use dp for this. what are the states?
I understand what he's saying. Everyone here is trying to solve the problem by picking one starting point, but the truth is, you can have any vertice be a starting point. If you were to assume addition as the only operation for simplicty sake, you could start at each vertice 1,2,3...N and have an NxN matrix from this system of equations.
You put each successive vertice in the diagonal because:
1.) Row 1 is the value of the each vertice after performing an addition operation, starting from vertice on E1
2.) Row 2 is the value of each vertice, starting from the vertice ending at E1 and beginning at E2.
3.) If you follow this process, you have a system of equations that form a matrix. Solving for the right most column in one row, will produce the maximum value.
My thoughts on this question: I don't think they ever wanted you to write code. I think they wanted to see if you understand the problem. But ...I could be wrong.
So are you saying that this problem is just about where to start first and then compute them vertex by vertex sequentially? I'm afraid you can't just do it just sequentially. say 1+2+3*4+5*1, you have to both compute 1+2+3 and 4+5 first, then multiply them. and there may also be negative numbers which can't be treated by adding first.
@zyfo2, here is the solution of your example :
input is 1+2+3*4+5*1
so we create a matrix of size 6*6
with all these operations we get following matrix call it M[1:6,1:6]
1..3..6..24..54..54
....2..5..20..45..45
........3..12..27..27
..............4....9....9
....................5....5
..........................1
Now you man ask me how do i decide the resultant operator, the resultant operator is decided based upon from where we want to split the operation,
for example for calculation of M[1,5] we do following
M[1,5] = max(1+45,3+27,6*9,24+5) = 54
I get you. but you are only calculating the max for 1:5 and 2:6, it's also possible the max will be from a different starting point.
for example (-1) * 2 +3 *(-4) + (-5) +(-1) you can't get the max from 1:5 or 2:6, the max should be ((-5)+(-1)*2+(3))*(-4). I posted my solution already. you can check it.
Sonesh, Why a DP solution is required which has Time=O(n*n) and Space=O(n*n)? One could solve it in constant space by a brute force method.
We know: To compute the final value from a particular starting node/ vertex (say V(i) ), it is necessary to sequentially computed the result. This computation process will take O(n) time for each vertex.
Now, if we do this for 'n' different starting vertices, we have O(n*n) time. We see that no extra space is utilized in this algorithm which can help us reduce time complexity.
Sonesh - Your approach is not up to the expectation. It takes lot of assumptions. You have to understand that we can merge two vertices into "one vertex", in any direction, and at every step of reducing the polygon size by one vertex. There are certain basics of dynamic programming which a problem needs to qualify so that it can be solvable using this technique. I doubt this problem qualifies for dynamic programming.
Please justify your approach by writing a pseudo code and it should meet expectations of the problem submitted.
xcode - Your approach is acceptable only if we have a constraint wherein we are not allowed to change the direction while computing the total value. That is, if we started clockwise, we have to compute it clockwise and sequentially; If we started anti-clockwise, we have to compute it anti-clockwise and sequentially. This constraint is not given the problem submitted! Think of something without having this constraint.
The last step is not clear what is the expectation.
But I think the you should make all the + operations first and after the * to calculate the maximum summary. (assumption the numbers>1 and all integers.)
So, Do first all the + operations. It's just 0(n).
After that we will have only * operations. Do all of them. O(n)
You will have the result. With O(2n)= O(n).
There would be negative values in the vertexes, so doing + firstly is not the optimal solution.
The way I see it is as follows:
- call the vertices of the polygon P: p0, p1, ..., pn
- think of P as a vertex V0
- from this vertex V0 you have n edges, each one going to a n different vertices: V1_1, V1_2, .., V1_n
- V1_i is the polygon resulting from removing edge i from P
- the edge between V0 and V1_i is the result of the operation marked by edge i: oper(pi-1, pi)
- from each V1_i you have n-1 new edges to n-1 new vertices
- continue forming the "super graph" until you have a polygon of 2 edged with 2 vertices
What we are looking for is for the longest path with the heaviest weight.
In this super graph we have n! vertices, and the problem is NP.
I think we can use just greedy approach. I.e. for polygon with k edges find a pair (p, q) which produces the maximum number: (p ,q) = max ({i operation j : i, j - adjacent edges)
Then just call a recursion on polygons:
1. Let function CollapseMaxPair( P(k) ) - gets polygon with k edges and returns 'collapsed' polygon with k-1 edges
2. Then our recursion:
P = P(N);
Releat until two edges left
P = CollapseMaxPair( P )
maxvalue = max ( two remained values)
What do you think?
If the numbers are a and b then:
- if a <= b <= 0 --> use *
- if a < 0 < b --> use +
- if 0 == a and 0<= b --> use +
- if 1 == a and 1<= b --> use +
- else /* 1 < a <=b */ --> use *
And then does not matter which edge you start with.
1. choose 1 gabage value (for the last operation).
- if the number of (-) vertexs is 2k, the gabage vertex should be the smallest (+) vertex.
- if the number of (-) vertexs is 2k+1, the gabage vertex should be the biggest (-) vertex.
2. if the vertex value is either 0 or 1 use '+'
3. else use '*' operator..
(2.3 from Karthik Vvs's)
You can do this in O(2n).
Start at any node. Push back that node's value on to a vector. When you traverse to the next node, perform the operation that takes you to that node on every number in the vector with the value of the next node. Repeat until you are back at your starting node.
The value at index 1 of your vector is the value for the starting node, it's your current best_max. Then, for each entry in the vector, move to the next node in the polygon, performing the correct operation to get you to that node on every entry in the vector. Before you move on to the next vector entry, compare against your best_max. best_max will have the answer.
traverse each node and merge edges in this order.
1. with both vertices -ve and (*) op, as this will increase the +ve vertices in polygon.
2. with one vertices +ve and one -ve and op (+). this will try to nullify the -ve of nodes.
3. with one vertices +ve and one -ve and op (*).
4.with both vertices -ve and (+) op,
as in 4 pass we will merge all the edges with maximum result.
As the question is to find the maximum value..
1.if the vertex value is either 0 or 1 use '+'
2.else use '*' operator..
3.for example if the vertices values are in order 0,4,1,3,2,5..then the max result is ((0+4)+1)*3*2*5
code is as follows:
int result=0;
for each vertex in polygon P:
if(vertex.data==0 || vertex.data==1)res+=vertex.data; //considering vertex as a node
else res*=vertex.data;
return res;
A variation of the matrix chain multiplication should work.
Let the number at each vertex and the (clockwise) next edge be a single element.
Initialize a 2D matrix m[NxN]
- Kyuubi January 17, 2013Let m[i,i] = pol[i] for 1 <= i <= N
Let m[i,j] be the maximum value that can be achieved from the subset of elements from index i to index j.
Using the recursive equation :
m[i,j] = max { m[i,k] [operation for element at index k] m[k+1,j] } :: for 1 <= i <= k <= j <= N
This would give the maximum value if the list was linear, but since it is a circular linked list, repeat the entire procedure for the different start points in the circular linked list.