Algorithm to find out all the possible positions - algorithm

I need an algorithm to find out all the possible positions of a group of pieces in a chessboard. Like finding all the possible combinations of the positions of a number N of pieces.
For example in a chessboard numbered like cartesian coordinate systems any piece would be in a position
(x,y) where 1 <= x <= 8 and 1 <= y <= 8
I'd like to get an algorithm which can calculate for example for 3 pieces all the possible positions of the pieces in the board. But I don't know how can I get them in any order. I can get all the possible positions of a single piece but I don't know how to mix them with more pieces.
for(int i = 0; i<= 8; i++){
for(int j = 0; j<= 8; j++){
System.out.println("Position: x:"+i+", y:"+j);
}
}
How can I get a good algoritm to find all the posible positions of the pieces in a chessboard?
Thanks.

You got 8x8 board, so total of 64 squares.
Populate a list containing these 64 sqaures [let it be list], and find all of the possibilities recursively: Each step will "guess" one point, and invoke the recursve call to find the other points.
Pseudo code:
choose(list,numPieces,sol):
if (sol.length == numPieces): //base clause: print the possible solution
print sol
return
for each point in list:
sol.append(point) //append the point to the end of sol
list.remove(point)
choose(list,numPieces,sol) //recursive call
list.add(point) //clean up environment before next recursive call
sol.removeLast()
invoke with choose(list,numPieces,[]) where list is the pre-populated list with 64 elements, and numPieces is the pieces you are going to place.
Note: This solution assumes pieces are not identical, so [(1,2),(2,1)] and [(2,1),(1,2)] are both good different solutions.
EDIT:
Just a word about complexity, since there are (n^2)!/(n^2-k)! possible solutions for your problem - and you are looking for all of them, any algorithm will suffer from exponential run time, so trying to invoke it with just 10 pieces, will take ~400 years
[In the above notation, n is the width and length of the board, and k is the number of pieces]

You can use a recursive algorithm to generate all possiblities:
void combine(String instr, StringBuffer outstr, int index)
{
for (int i = index; i < instr.length(); i++)
{
outstr.append(instr.charAt(i));
System.out.println(outstr);
combine(instr, outstr, i + 1);
outstr.deleteCharAt(outstr.length() - 1);
}
}
combine("abc", new StringBuffer(), 0);

As I understand you should consider that some firgure may come block some potential position for figures that can reach them on the empty board. I guess it is the most tricky part.
So you should build some set of vertexes (set of board states) that is reached from some single vertex (initial board state).
The first algorithm that comes to my mind:
Pre-conditions:
Order figures in some way to form circle.
Assume initial set of board states (S0) to contain single element which represents inital board state.
Actions
Choose next figure to extend set of possible positions
For each state of board within S(n) walk depth-first all possible movements that new board states and call it F(n) (frame).
Form S(n+1) = S(n) ∪ F(n).
Repeat steps till all frames of updates during whole circle pass will not be empty.
This is kind of mix breath-first and depth-first search

Related

Understanding subtleties of dynamic programming approaches

I understand that there are mainly two approaches to dynamic programming solutions:
Fixed optimal order of evaluation (lets call it Foo approach): Foo approach usually goes from subproblems to bigger problems thus using results obtained earlier for subproblems to solve bigger problems, thus avoiding "revisiting" subproblem. CLRS also seems to call this "Bottom Up" approach.
Without fixed optimal order of evaluation (lets call it Non-Foo approach): In this approach evaluation proceeds from problems to their sub-problems . It ensures that sub problems are not "re-evaluated" (thus ensuring optimality) by maintaining results of their past evaluations in some data structure and then first checking if the result of the problem at hand exists in this data structure before starting its evaluation. CLRS seem to call this as "Top Down" approach
This is what is roughly conveyed as one of the main points by this answer.
I have following doubts:
Q1. Memoization or not?
CLRS uses terms "top down with memoization" approach and "bottom up" approach. I feel both approaches require memory to cache results of sub problems. But, then, why CLRS use term "memoization" only for top down approach and not for bottom up approach? After solving some problems by DP approach, I feel that solutions by top down approach for all problems require memory to caches results of "all" subproblems. However, that is not the case with bottom up approach. Solutions by bottom up approach for some problems does not need to cache results of "all" sub problems. Q1. Am I correct with this?
For example consider this problem:
Given cost[i] being the cost of ith step on a staircase, give the minimum cost of reaching the top of the floor if:
you can climb either one or two steps
you can start from the step with index 0, or the step with index 1
The top down approach solution is as follows:
class Solution:
def minCostAux(self, curStep, cost):
if self.minCosts[curStep] > -1:
return self.minCosts[curStep]
if curStep == -1:
return 0
elif curStep == 0:
self.minCosts[curStep] = cost[0]
else:
self.minCosts[curStep] = min(self.minCostAux(curStep-2, cost) + cost[curStep]
, self.minCostAux(curStep-1, cost) + cost[curStep])
return self.minCosts[curStep]
def minCostClimbingStairs(self, cost) -> int:
cost.append(0)
self.minCosts = [-1] * len(cost)
return self.minCostAux(len(cost)-1, cost)
The bottom up approach solution is as follows:
class Solution:
def minCostClimbingStairs(self, cost) -> int:
cost.append(0)
secondLastMinCost = cost[0]
lastMinCost = min(cost[0]+cost[1], cost[1])
minCost = lastMinCost
for i in range(2,len(cost)):
minCost = min(lastMinCost, secondLastMinCost) + cost[i]
secondLastMinCost = lastMinCost
lastMinCost = minCost
return minCost
Note that the top down approach caches result of all steps in self.minCosts while bottom up approach caches result of only last two steps in variables lastMinCost and secondLastMinCost.
Q2. Does all problems have solutions by both approaches?
I feel no. I came to this opinion after solving this problem:
Find the probability that the knight will not go out of n x n chessboard after k moves, if the knight was initially kept in the cell at index (row, column).
I feel the only way to solve this problem is to find successive probabilities in increasing number of steps starting from cell (row, column), that is probability that the knight will not go out of chessboard after step 1, then after step 2, then after step 3 and so on. This is bottom up approach. We cannot do it top down, for example, we cannot start with kth step and go to k-1th step, then k-2th step and so on, because:
We cannot know which cells will be reached in kth step to start with
We cannot ensure that all paths from kth step will lead to initial knight cell position (row,column).
Even one of the top voted answer gives dp solution as follows:
class Solution {
private int[][]dir = new int[][]{{-2,-1},{-1,-2},{1,-2},{2,-1},{2,1},{1,2},{-1,2},{-2,1}};
private double[][][] dp;
public double knightProbability(int N, int K, int r, int c) {
dp = new double[N][N][K + 1];
return find(N,K,r,c);
}
public double find(int N,int K,int r,int c){
if(r < 0 || r > N - 1 || c < 0 || c > N - 1) return 0;
if(K == 0) return 1;
if(dp[r][c][K] != 0) return dp[r][c][K];
double rate = 0;
for(int i = 0;i < dir.length;i++) rate += 0.125 * find(N,K - 1,r + dir[i][0],c + dir[i][1]);
dp[r][c][K] = rate;
return rate;
}
}
I feel this is still a bottom up approach since it starts with initial knight cell position (r,c) (and hence starts from 0th or no step to Kth step) despite the fact that it counts K downwads to 0. So, this is bottom up approach done recursively and not top down approach. To be precise, this solution does NOT first find:
probability of knight not going out of chessboard after K steps starting at cell (r,c)
and then find:
probability of knight not going out of chessboard after K-1 steps starting at cell (r,c)
but it finds in reverse / bottom up order: first for K-1 steps and then for K steps.
Also, I did not find any solutions in of top voted discussions in leetcode doing it in truly top down manner, starting from Kth step to 0th step ending in (row,column) cell, instead of starting with (row,column) cell.
Similarly we cannot solve the following problem with the bottom up approach but only with top down approach:
Find the probability that the Knight ends up in the cell at index (row,column) after K steps, starting at any initial cell.
Q2. So am I correct with my understanding that not all problems have solutions by both top down or bottom up approaches? Or am I just overthinking unnecessarily and both above problems can indeed be solved with both top down and bottom up approaches?
PS: I indeed seem to have done overthinking here: knightProbability() function above is indeed top down, and I ill-interpreted as explained in detailed above 😑. I have kept this explanation for reference as there are already some answers below and also as a hint of how confusion / mis-interpretaions might happen, so that I will be more cautious in future. Sorry if this long explanation caused you some confusion / frustrations. Regardless, the main question still holds: does every problem have bottom up and top down solutions?
Q3. Bottom up approach recursively?
I am pondering if bottom up solutions for all problems can also be implemented recursively. After trying to do so for other problems, I came to following conclusion:
We can implement bottom up solutions for such problems recursively, only that the recursion won't be meaningful, but kind of hacky.
For example, below is recursive bottom up solution for minimum cost climbing stairs problem mentioned in Q1:
class Solution:
def minCostAux(self, step_i, cost):
if self.minCosts[step_i] != -1:
return self.minCosts[step_i]
self.minCosts[step_i] = min(self.minCostAux(step_i-1, cost)
, self.minCostAux(step_i-2, cost)) + cost[step_i]
if step_i == len(cost)-1: # returning from non-base case, gives sense of
# not-so meaningful recursion.
# Also, base cases usually appear at the
# beginning, before recursive call.
# Or should we call it "ceil condition"?
return self.minCosts[step_i]
return self.minCostAux(step_i+1, cost)
def minCostClimbingStairs(self, cost: List[int]) -> int:
cost.append(0)
self.minCosts = [-1] * len(cost)
self.minCosts[0] = cost[0]
self.minCosts[1] = min(cost[0]+cost[1], cost[1])
return self.minCostAux(2, cost)
Is my quoted understanding correct?
First, context.
Every dynamic programming problem can be solved without dynamic programming using a recursive function. Generally this will take exponential time, but you can always do it. At least in principle. If the problem can't be written that way, then it really isn't a dynamic programming problem.
The idea of dynamic programming is that if I already did a calculation and have a saved result, I can just use that saved result instead of doing the calculation again.
The whole top-down vs bottom-up distinction refers to the naive recursive solution.
In a top-down approach your call stack looks like the naive version except that you make a "memo" of what the recursive result would have given. And then the next time you short-circuit the call and return the memo. This means you can always, always, always solve dynamic programming problems top down. There is always a solution that looks like recursion+memoization. And that solution by definition is top down.
In a bottom up approach you start with what some of the bottom levels would have been and build up from there. Because you know the structure of the data very clearly, frequently you are able to know when you are done with data and can throw it away, saving memory. Occasionally you can filter data on non-obvious conditions that are hard for memoization to duplicate, making bottom up faster as well. For a concrete example of the latter, see Sorting largest amounts to fit total delay.
Start with your summary.
I strongly disagree with your thinking about the distinction in terms of the optimal order of evaluations. I've encountered many cases with top down where optimizing the order of evaluations will cause memoization to start hitting sooner, making code run faster. Conversely while bottom up certainly picks a convenient order of operations, it is not always optimal.
Now to your questions.
Q1: Correct. Bottom up often knows when it is done with data, top down does not. Therefore bottom up gives you the opportunity to delete data when you are done with it. And you gave an example where this happens.
As for why only one is called memoization, it is because memoization is a specific technique for optimizing a function, and you get top down by memoizing recursion. While the data stored in dynamic programming may match up to specific memos in memoization, you aren't using the memoization technique.
Q2: I do not know.
I've personally found cases where I was solving a problem over some complex data structure and simply couldn't find a bottom up approach. Maybe I simply wasn't clever enough, but I don't believe that a bottom up approach always exists to be found.
But top down is always possible. Here is how to do it in Python for the example that you gave.
First the naive recursive solution looks like this:
def prob_in_board(n, i, j, k):
if i < 0 or j < 0 or n <= i or n <= j:
return 0
elif k <= 0:
return 1
else:
moves = [
(i+1, j+2), (i+1, j-2),
(i-1, j+2), (i-1, j-2),
(i+2, j+1), (i+2, j-1),
(i-2, j+1), (i-2, j-1),
]
answer = 0
for next_i, next_j in moves:
answer += prob_in_board(n, next_i, next_j, k-1) / len(moves)
return answer
print(prob_in_board(8, 3, 4, 7))
And now we just memoize.
def prob_in_board_memoized(n, i, j, k, cache=None):
if cache is None:
cache = {}
if i < 0 or j < 0 or n <= i or n <= j:
return 0
elif k <= 0:
return 1
elif (i, j, k) not in cache:
moves = [
(i+1, j+2), (i+1, j-2),
(i-1, j+2), (i-1, j-2),
(i+2, j+1), (i+2, j-1),
(i-2, j+1), (i-2, j-1),
]
answer = 0
for next_i, next_j in moves:
answer += prob_in_board_memoized(n, next_i, next_j, k-1, cache) / len(moves)
cache[(i, j, k)] = answer
return cache[(i, j, k)]
print(prob_in_board_memoized(8, 3, 4, 7))
This solution is top down. If it seems otherwise to you, then you do not correctly understand what is meant by top-down.
I found your question ( does every dynamic programming problem have bottom up and top down solutions ? ) very interesting. That's why I'm adding another answer to continue the discussion about it.
To answer the question in its generic form, I need to formulate it more precisely with math. First, I need to formulate precisely what is a dynamic programming problem. Then, I need to define precisely what is a bottom up solution and what is a top down solution.
I will try to put some definitions but I think they are not the most generic ones. I think a really generic definition would need more heavy math.
First, define a state space S of dimension d as a subset of Z^d (Z represents the integers set).
Let f: S -> R be a function that we are interested in calculate for a given point P of the state space S (R represents the real numbers set).
Let t: S -> S^k be a transition function (it associates points in the state space to sets of points in the state space).
Consider the problem of calculating f on a point P in S.
We can consider it as a dynamic programming problem if there is a function g: R^k -> R such that f(P) = g(f(t(P)[0]), f(t(P)[1]), ..., f(t(P)[k])) (a problem can be solved only by using sub problems) and t defines a directed graph that is not a tree (sub problems have some overlap).
Consider the graph defined by t. We know it has a source (the point P) and some sinks for which we know the value of f (the base cases). We can define a top down solution for the problem as a depth first search through this graph that starts in the source and calculate f for each vertex at its return time (when the depth first search of all its sub graph is completed) using the transition function. On the other hand, a bottom up solution for the problem can be defined as a multi source breadth first search through the transposed graph that starts in the sinks and finishes in the source vertex, calculating f at each visited vertex using the previous visited layer.
The problem is: to navigate through the transposed graph, for each point you visit you need to know what points transition to this point in the original graph. In math terms, for each point Q in the transition graph, you need to know the set J of points such that for each point Pi in J, t(Pi) contains Q and there is no other point Pr in the state space outside of J such that t(Pr) contains Q. Notice that a trivial way to know this is to visit all the state space for each point Q.
The conclusion is that a bottom up solution as defined here always exists but it only compensates if you have a way to navigate through the transposed graph at least as efficiently as navigating through the original graph. This depends essentially in the properties of the transition function.
In particular, for the leetcode problem you mentioned, the transition function is the function that, for each point in the chessboard, gives all the points to which the knight can go to. A very special property about this function is that it's symmetric: if the knight can go from A to B, then it can also go from B to A. So, given a certain point P, you can know to which points the knight can go as efficiently as you can know from which points the knight can come from. This is the property that guarantees you that there exists a bottom up approach as efficient as the top down approach for this problem.
For the leetcode question you mentioned, the top down approach is like the following:
Let P(x, y, k) be the probability that the knight is at the square (x, y) at the k-th step. Look at all squares that the knight could have come from (you can get them in O(1), just look at the board with a pen and paper and get the formulas from the different cases, like knight in the corner, knight in the border, knight in a central region etc). Let them be (x1, y1), ... (xj, yj). For each of these squares, what is the probability that the knight jumps to (x, y) ? Considering that it can go out of the border, it's always 1/8. So:
P(x, y, k) = (P(x1, y1, k-1) + ... + P(xj, yj, k-1))/8
The base case is k = 0:
P(x, y ,0) = 1 if (x, y) = (x_start, y_start) and P(x, y, 0) = 0 otherwise.
You iterate through all n^2 squares and use the recurrence formula to calculate P(x, y, k). Many times you will need solutions you already calculated for k-1 and so you can benefit a lot from memoization.
In the end, the final solution will be the sum of P(x, y, k) over all squares of the board.

What is a greedy algorithm for this problem that is minimally optimal + proof?

The details are a bit cringe, fair warning lol:
I want to set up meters on the floor of my building to catch someone; assume my floor is a number line from 0 to length L. The specific type of meter I am designing has a radius of detection that is 4.7 meters in the -x and +x direction (diameter of 9.4 meters of detection). I want to set them up in such a way that if the person I am trying to find steps foot anywhere in the floor, I will know. However, I can't just setup a meter anywhere (it may annoy other residents); therefore, there are only n valid locations that I can setup a meter. Additionally, these meters are expensive and time consuming to make, so I would like to use as few as possible.
For simplicity, you can assume the meter has 0 width, and that each valid location is just a point on the number line aformentioned. What is a greedy algorithm that places as few meters as possible, while being able to detect the entire hallway of length L like I want it to, or, if detecting the entire hallway is not possible, will output false for the set of n locations I have (and, if it isn't able to detect the whole hallway, still uses as few meters as possible while attempting to do so)?
Edit: some clarification on being able to detect the entire hallway or not
Given:
L (hallway length)
a list of N valid positions to place a meter (p_0 ... p_N-1) of radius 4.7
You can determine in O(N) either a valid and minimal ("good") covering of the whole hallway or a proof that no such covering exists given the constraints as follows (pseudo-code):
// total = total length;
// start = current starting position, initially 0
// possible = list of possible meter positions
// placed = list of (optimal) meter placements, initially empty
boolean solve(float total, float start, List<Float> possible, List<Float> placed):
if (total-start <= 0):
return true; // problem solved with no additional meters - woo!
else:
Float next = extractFurthestWithinRange(start, possible, 4.7);
if (next == null):
return false; // no way to cover end of hall: report failure
else:
placed.add(next); // placement decided
return solve(total, next + 4.7, possible, placed);
Where extractFurthestWithinRange(float start, List<Float> candidates, float range) returns null if there are no candidates within range of start, or returns the last position p in candidates such that p <= start + range -- and also removes p, and all candidates c such that p >= c.
The key here is that, by always choosing to place a meter in the next position that a) leaves no gaps and b) is furthest from the previously-placed position we are simultaneously creating a valid covering (= no gaps) and an optimal covering (= no possible valid covering could have used less meters - because our gaps are already as wide as possible). At each iteration, we either completely solve the problem, or take a greedy bite to reduce it to a (guaranteed) smaller problem.
Note that there can be other optimal coverings with different meter positions, but they will use the exact same number of meters as those returned from this pseudo-code. For example, if you adapt the code to start from the end of the hallway instead of from the start, the covering would still be good, but the gaps could be rearranged. Indeed, if you need the lexicographically minimal optimal covering, you should use the adapted algorithm that places meters starting from the end:
// remaining = length (starts at hallway length)
// possible = positions to place meters at, starting by closest to end of hallway
// placed = positions where meters have been placed
boolean solve(float remaining, List<Float> possible, Queue<Float> placed):
if (remaining <= 0):
return true; // problem solved with no additional meters - woo!
else:
// extracts points p up to and including p such that p >= remaining - range
Float next = extractFurthestWithinRange2(remaining, possible, 4.7);
if (next == null):
return false; // no way to cover start of hall: report failure
else:
placed.add(next); // placement decided
return solve(next - 4.7, possible, placed);
To prove that your solution is optimal if it is found, you merely have to prove that it finds the lexicographically last optimal solution.
And you do that by induction on the size of the lexicographically last optimal solution. The case of a zero length floor and no monitor is trivial. Otherwise you demonstrate that you found the first element of the lexicographically last solution. And covering the rest of the line with the remaining elements is your induction step.
Technical note, for this to work you have to be allowed to place monitoring stations outside of the line.

How to improve this Dynamic programming solution(Optimisation in algorithm)

The Problem statement:
Assurance Company of Moving (ACM) is a company of moving things for people. Recently, some schools want to move their computers to another place. So they ask ACM to help them. One school reserves K trucks for moving, and it has N computers to move. In order not to waste the trucks, the school ask ACM to use all the trucks. That is to say, there must be some computers in each truck, and there are no empty trucks. ACM wants to know how many partition shemes exists with moving N computers by K trucks, the ACM ask you to compute the number of different shemes with given N and K. You needn't care with the order. For example N=7,K=3, the the following 3 partition instances are regarded as the same one and should be counted as one sheme: "1 1 5","1 5 1","5 1 1". Each truck can carry almost unlimited computers!!
Save Time :
You have to count how many sequences a[1..k] exist such that :
1) a[i] + a[2] + .... + a[k] = N such that permutations dont matter
My O(N*K^2) solution (Cannot figure out how to improve on it)
#include<assert.h>
#include<stdio.h>
#include<algorithm>
using namespace std;
int DP[5001][5001];
void ini()
{
int i,j,k;
DP[0][0]=1;
for(k=1;k<=500;k++)
for(j=1;j<=500;j++)
for(i=1;i<=500;i++)
{
DP[i][j]+=j>=k?DP[i-1][j-k]:0;
DP[i][j]%=1988;
}
return ;
}
int main()
{
ini();
int N,K,i,j;
while(1)
{
scanf("%d%d",&N,&K);
if(N==0 && K==0)
return 0;
int i;
if(DP[K][N]==0)
{assert(0);}
printf("%d\n",DP[K][N]);
}
return 0;
}
Explanation of my solution DP[i][j] represents the number of ways I can have total j computers using i Trucks only.
The k represents the number of computers with which I am dealing with that means I am just avoiding permutations!
How can I improve it to O(N*K)?
Problem constraints
N (1<=N<=5000) and K(1<=K<=N)
Problem Link: Problem Spoj
Just say that you have K gift boxes and N chocolates.
I will start with a recursive and real easy to convert it to iterative solution.
The key to avoid repetitions is distributing chocolates in a ascending order (descending also works). So you 7 chocolates and I put 2 chocolate in the first box, I will put at least 2 in the second box. WHY? this helps in avoiding repetitions.
now onwards TCL = totalChocholatesLeft & TBL = totalBinsLeft
So S(TCL,TBL) = S(TCL-TBL,TBL) + S(TCL,TBL-1);
you have to call the above expression starting with S(n-k), k)
Why? because all boxes need at least one item so first put `1` each box.
Now you are left with only `n-k` chocolates.
That's all! that's the DP recursion.
How does it work?
So in order to remove repetitions we are maintaning the ascending order.
What is the easiest way to maintain the ascending order ?
If you put 1 chocolate in the ith box, put 1 in all boxes in front of it i+1, i++2 .....k.
So after keeping chocolate in a gift box, you have two choices :
Either you want to continue with current box :
S(TCL-TBL,TBL) covers this
or to move the next box just never consider this box again
S(TCL,TBL-1) covers this.
Equivalent DP would make have TC : O(NK)
This problem is equivalent to placing n-k identical balls (after already placing one ball in each cell to make sure it's not empty) in k identical cells.
This can be solved using the recurrence formula:
D(n,0) = 0 n > 0
D(n,k) = 0 n < 0
D(n,1) = 1 n >= 0
D(n,k) = D(n,k-1) + D(n-k,k)
Explanation:
Stop clauses:
D(n,0) - no way to put n>0 balls in 0 cells
D(n<0,k) - no way to put negative number of balls in k cells
D(n,1) - one way to put n balls in 1 cell: all in this cell
Recurrence:
We have two choices.
We either have one (or more) empty cell, so we recurse with the same problem, and one less cell: D(n,k-1)
Otherwise, we have no empty cells, so we put one ball in each cell, recurse with the same number of cells and k less balls, D(n-k,k)
The two possibilities are of disjoint sets, so the union of both sets is the summation of the two sizes, thus D(n,k) = D(n,k-1) + D(n-k,k)
The above recursive formula is easy to compute in O(1) (assuming O(1) arithmetics), if the "lower" problems are known, and the DP solution needs to fill a table of size (n+1)*(k+1), so this solution is O(nk)

Understanding solution to finding optimal strategy for game involving picking pots of gold

I am having trouble understanding the reasoning behind the solution to this question on CareerCup.
Pots of gold game: Two players A & B. There are pots of gold arranged
in a line, each containing some gold coins (the players can see how
many coins are there in each gold pot - perfect information). They get
alternating turns in which the player can pick a pot from one of the
ends of the line. The winner is the player which has a higher number
of coins at the end. The objective is to "maximize" the number of
coins collected by A, assuming B also plays optimally. A starts the
game.
The idea is to find an optimal strategy that makes A win knowing that
B is playing optimally as well. How would you do that?
At the end I was asked to code this strategy!
This was a question from a Google interview.
The proposed solution is:
function max_coin( int *coin, int start, int end ):
if start > end:
return 0
// I DON'T UNDERSTAND THESE NEXT TWO LINES
int a = coin[start] + min(max_coin(coin, start+2, end), max_coin(coin, start+1, end-1))
int b = coin[end] + min(max_coin(coin, start+1,end-1), max_coin(coin, start, end-2))
return max(a,b)
There are two specific sections I don't understand:
In the first line why do we use the ranges [start + 2, end] and [start + 1, end - 1]? It's always leaving out one coin jar. Shouldn't it be [start + 1, end] because we took the starting coin jar out?
In the first line, why do we take the minimum of the two results and not the maximum?
Because I'm confused about why the two lines take the minimum and why we choose those specific ranges, I'm not really sure what a and b actually represent?
First of all a and b represent respectively the maximum gain if start (respectively end) is played.
So let explain this line:
int a = coin[start] + min(max_coin(coin, start+2, end), max_coin(coin, start+1, end-1))
If I play start, I will immediately gain coin[start]. The other player now has to play between start+1 and end. He plays to maximize his gain. However since the number of coin is fixed, this amounts to minimize mine. Note that
if he plays start+1 I'll gain max_coin(coin, start+2, end)
if he plays end Ill gain max_coin(coin, start+1, end-1)
Since he tries to minimize my gain, I'll gain the minimum of those two.
Same reasoning apply to the other line where I play end.
Note: This is a bad recursive implementation. First of all max_coin(coin, start+1, end-1) is computed twice. Even if you fix that, you'll end up computing lots of time shorter case. This is very similar to what happens if you try to compute Fibonacci numbers using recursion. It would be better to use memoization or dynamic programming.
a and b here represent the maximum A can get by picking the starting pot or the ending pot, respectively.
We're actually trying to maximize A-B, but since B = TotalGold - A, we're trying to maximize 2A - TotalGold, and since TotalGold is constant, we're trying to maximize 2A, which is the same as A, so we completely ignore the values of B's picks and just work with A.
The updated parameters in the recursive calls include B picking as well - so coin[start] represents A picking the start, then B picks the next one from the start, so it's start+2. For the next call, B picks from the end, so it's start+1 and end-1. Similarly for the rest.
We're taking the min, because B will try to maximize it's own profit, so it will pick the choice that minimizes A's profit.
But actually I'd say this solution is lacking a bit in the sense that it just returns a single value, not 'an optimal strategy', which, in my mind, would be a sequence of moves. And it also doesn't take into account the possibility that A can't win, in which case one might want to output a message saying that it's not possible, but this would really be something to clarify with the interviewer.
Let me answer your points in reverse order, somehow it seems to make more sense that way.
3 - a and b represent the amount of coins the first player will get, when he/she chooses the first or the last pot respectively
2 - we take the minimum because it is the choice of the second player - he/she will act to minimise the amount of coins the first player will get
1 - the first line presents the scenario - if the first player has taken the first pot, what will the second player do? If he/she again takes the first pot, it will leave (start+2, end). If he/she takes the last pot, it will leave (start+1, end-1)
Assume what you gain on your turn is x and what you get in all consequent turns is y. Both values represent x+y, where a assumes you take next pot (x=coin[start]) from the front and b assumes you take your next pot (x=coin[end]) from the back.
Now how you compute y.
After your choice, the opponent will use the same optimum strategy (thus recursive calls) to maximise his profit, and you will be left with a the smaller profit for the turn. This is why your y=min(best_strategy_front(), best_strategy_end()) -- your value is the smaller of the two choices that are left because the opponent will take the bigger.
The indexing simply indicates the remaining sequences minus one pot on the front and on the back after you made your choice.
A penny from my end too. I have explained steps in detail.
public class Problem08 {
static int dp[][];
public static int optimalGameStrategy(int arr[], int i, int j) {
//If one single element then choose that.
if(i == j) return arr[i];
//If only two elements then choose the max.
if (i + 1 == j ) return Math.max(arr[i], arr[j]);
//If the result is already computed, then return that.
if(dp[i][j] != -1) return dp[i][j];
/**
* If I choose i, then the array length will shrink to i+1 to j.
* The next move is of the opponent. And whatever he choose, I would want the result to be
* minimum. If he choose j, then array will shrink to i+1, j-1. But if also choose i then
* array will shrink to i+2,j. Whatever he choose, I want the result to be min, hence I take
* the minimum of his two choices.
*
* Similarly for a case, when I choose j.
*
* I will eventually take the maximum of both of my case. :)
*/
int iChooseI = arr[i] + Math.min(optimalGameStrategy(arr, i+1, j-1),
optimalGameStrategy(arr, i+2, j));
int iChooseJ = arr[j] + Math.min(optimalGameStrategy(arr, i+1, j-1),
optimalGameStrategy(arr, i, j-2));
int res = Math.max(iChooseI, iChooseJ );
dp[i][j] = res;
return res;
}
public static void main(String[] args) {
int[] arr = new int[]{5,3,7,10};
dp = new int[arr.length][arr.length];
for(int i=0; i < arr.length; i++) {
for(int j=0; j < arr.length; j++) {
dp[i][j] = -1;
}
}
System.out.println( " Nas: " + optimalGameStrategy(arr, 0, arr.length-1));
}
}

Selecting evenly distributed points algorithm

Suppose there are 25 points in a line segment, and these points may be unevenly distributed (spatially) as the following figure shows:
My question is how we can select 10 points among these 25 points so that these 10 points can be as spatially evenly distributed as possible. In the idea situation, the selected points should be something like this:
EDIT:
It is true that this question can become more elegant if I can tell the criterion that justify the "even distribution". What I know is my expection for the selected points: if I divide the line segment into 10 equal line segments. I expect there should be one point on each small line segment. Of course it may happen that in some small line segments we cannot find representative points. In that case I will resort to its neighboring small line segment that has representative point. In the next step I will further divide the selected neighboring segment into two parts: if each part has representative points, then the empty representative point problem will be solved. If we cannot find representative point in one of the small line segments, we can further divide it into smaller parts. Or we can resort to the next neighboring line segment.
EDIT:
Using dynamic programming, a possible solution is implemented as follows:
#include <iostream>
#include <vector>
using namespace std;
struct Note
{
int previous_node;
double cost;
};
typedef struct Note Note;
int main()
{
double dis[25] =
{0.0344460805029088, 0.118997681558377, 0.162611735194631,
0.186872604554379, 0.223811939491137, 0.276025076998578,
0.317099480060861, 0.340385726666133, 0.381558457093008,
0.438744359656398, 0.445586200710900, 0.489764395788231,
0.498364051982143, 0.585267750979777, 0.646313010111265,
0.655098003973841, 0.679702676853675, 0.694828622975817,
0.709364830858073, 0.754686681982361, 0.765516788149002,
0.795199901137063, 0.823457828327293, 0.950222048838355, 0.959743958516081};
Note solutions[25];
for(int i=0; i<25; i++)
{
solutions[i].cost = 1000000;
}
solutions[0].cost = 0;
solutions[0].previous_node = 0;
for(int i=0; i<25; i++)
{
for(int j= i-1; j>=0; j--)
{
double tempcost = solutions[j].cost + std::abs(dis[i]-dis[j]-0.1);
if (tempcost<solutions[i].cost)
{
solutions[i].previous_node = j;
solutions[i].cost = tempcost;
}
}
}
vector<int> selected_points_index;
int i= 24;
selected_points_index.push_back(i);
while (solutions[i].previous_node != 0)
{
i = solutions[i].previous_node;
selected_points_index.push_back(i);
}
selected_points_index.push_back(0);
std::reverse(selected_points_index.begin(),selected_points_index.end());
for(int i=0; i<selected_points_index.size(); i++)
cout<<selected_points_index[i]<<endl;
return 0;
}
The result are shown in the following figure, where the selected points are denoted as green:
Until a good, and probably O(n^2) solution comes along, use this approximation:
Divide the range into 10 equal-sized bins. Choose the point in each bin closest to the centre of each bin. Job done.
If you find that any of the bins is empty choose a smaller number of bins and try again.
Without information about the scientific model that you are trying to implement it is difficult (a) to suggest a more appropriate algorithm and/or (b) to justify the computational effort of a more complicated algorithm.
Let {x[i]} be your set of ordered points. I guess what you need to do is to find the subset of 10 points {y[i]} that minimizes \sum{|y[i]-y[i-1]-0.1|} with y[-1] = 0.
Now, if you see the configuration as a strongly connected directed graph, where each node is one of the 25 doubles and the cost for every edge is |y[i]-y[i-1]-0.1|, you should be able to solve the problem in O(n^2 +nlogn) time with the Dijkstra's algorithm.
Another idea, that will probably lead to a better result, is using dynamic programming : if the element x[i] is part of our soltion, the total minimum is the sum of the minimum to get to the x[i] point plus the minimum to get the final point, so you could write a minimum solution for each point, starting from the smallest one, and using for the next one the minimum between his predecessors.
Note that you'll probably have to do some additional work to pick, from the solutions set, the subset of those with 10 points.
EDIT
I've written this in c#:
for (int i = 0; i < 25; i++)
{
for (int j = i-1; j > 0; j--)
{
double tmpcost = solution[j].cost + Math.Abs(arr[i] - arr[j] - 0.1);
if (tmpcost < solution[i].cost)
{
solution[i].previousNode = j;
solution[i].cost = tmpcost;
}
}
}
I've not done a lot of testing, and there may be some problem if the "holes" in the 25 elements are quite wide, leading to solutions that are shorter than 10 elements ... but it's just to give you some ideas to work on :)
You can find approximate solution with Adaptive Non-maximal Suppression (ANMS) algorithm provided the points are weighted. The algorithm selects n best points while keeping them spatially well distributed (most spread across the space).
I guess you can assign point weights based on your distribution criterion - e.g. a distance from uniform lattice of your choice. I think the lattice should have n-1 bins for optimal result.
You can look up following papers discussing the 2D case (the algorithm can be easily realized in 1D):
Turk, Steffen Gauglitz Luca Foschini Matthew, and Tobias Höllerer. "EFFICIENTLY SELECTING SPATIALLY DISTRIBUTED KEYPOINTS FOR VISUAL TRACKING."
Brown, Matthew, Richard Szeliski, and Simon Winder. "Multi-image matching using multi-scale oriented patches." Computer Vision and Pattern Recognition, 2005. CVPR 2005. IEEE Computer Society Conference on. Vol. 1. IEEE, 2005.
The second paper is less related to your problem but it describes basic ANMS algorithm. The first papers provides faster solution. I guess both will do in 1D for a moderate amount of points (~10K).

Resources