I'm trying to solve a problem from SPOJ ( link ), which can be briefly described like this: Given n intervals, each with an integer beginning and end, and given the end with max time ( let's call it max_end ) , find in how many ways you can choose a set of intervals that covers 1...max_end. Intervals may overlap. I tried a DP; first sort by end time, then dp[ i ] is a pair, where dp[ i ].first is the minimum number of intervals needed to cover 1...end[ i ] last using interval i and dp[ i ].second is the number of ways to do it. Here's my main DP loop:
for( int i = 1; i < n; i ++ ) {
for( int j = 0; j < i; j ++ ) {
if( ! ( x[ j ].end >= x[ i ].start - 1 ) )
continue;
if( dp[ j ].first + 1 < dp[ i ].first ) {
dp[ i ].first = dp[ j ].first + 1;
dp[ i ].second = dp[ j ].second;
}
else if( dp[ j ].first + 1 == dp[ i ].first ) {
dp[ i ].second += dp[ j ].second;
}
}
}
Unfortunately, it didn't work. Can somebody please tell me where I have a mistake? Thanks in advance! :)
I'm not sure I get your solution idea, but I describe my AC solution:
I'm using function with memorization, but you can re-write it using non-recurcive DP.
Let's say we have our intervals in array
pair a[100];
where
a[i].first is interval begin and a[i].second is interval end.
Sort this array by begin first (default behavior of stl sort algorithm with default pair comparator).
Now imagine that we are 'putting' intervals one by one from beginning to end.
let f(int x, int prev) return the number of ways to finish the filling if currently last interval is x and previous is 'prev'.
we'll calculate it as follows:
int f(int x, int prev) {
// if already calculated dp[x][prev], return it. Otherwise, calculate it
if (dp[x][prev] != -1) {
return dp[x][prev];
}
if (a[x].second == m) {
return dp[x][prev] = 1; // it means - X is last interval in day
}
else {
dp[x][prev] = 0;
for (int i = x + 1; i < n; ++i) { // try to select next interval
if (a[i].first <= a[x].second && // there must be not empty space after x interval
a[i].second > a[x].second && // if this is false, the set won't be minimal - i interval is useless
a[i].first > a[x].first && // if this is false, the set won't be minimal, x interval is useless
a[prev].second < a[i].first) { // if this is false, the set won't be minimal, x interval is useless.
dp[x][prev] = (dp[x][prev] + f(i, x)) % 100000000;
}
}
}
return dp[x][prev];
}
After that we need to call this function for every pair of intervals, first of which start at 0 and second is connected with first:
for (int i = 0; i < n; ++i) {
if (a[i].first == 0) {
for (int j = i + 1; j < n; ++j) {
if (a[j].first > 0 && // we don't need to start at 0 - in this case either i or j will be useless
a[j].first <= a[i].second && // there must be no space after i interval
a[j].second > a[i].second) { // in opposite case j will be useless
res = (res + f(j, i)) % 100000000;
}
}
// also we need to check the case when we use only one interval:
if (a[i].second == m) {
res = (res + 1) % 100000000;
}
}
}
After that we only need to print the res.
Related
I'm trying to implement the Join Five game. It is a game where, given a grid and a starting configuration of dots, you have to add dots in free crossings, so that each dot that you add forms a 5-dot line with those already in the grid. Two lines may only have 1 dot in common (they may cross or touch end to end)
My game grid is an int array that contains 0 or 1. 1 if there is a dot, 0 if there isn't.
I'm doing kinda well in the implementation, but I'd like to display all the possibles moves.
I made a very long and ugly function that is available here : https://pastebin.com/tw9RdNgi (it was way too long for my post i'm sorry)
here is a code snippet :
if(jeu->plat[i][j] == 0) // if we're on a empty spot
{
for(k = 0; k < lineSize; k++) // for each direction
{
//NORTH
if(jeu->plat[i-1-k][j] == 1) // if there is a dot north
{
n++; // we count it
}
else
{
break; //we change direction
}
} //
This code repeats itself 7 other times changing directions and if n or any other variable reaches 4 we count the x and y as a possible move.
And it's not even treating all the cases, if the available spot is between 2 and 2 dots it will not count it. same for 3 and 1 and 1 and 3.
But I don't think the way I started doing it is the best one. I'm pretty sure there is an easier and more optimized way but i can't figure it out.
So my question is: could somebody help me figure out how to find all the possible 5-dot alignments, or tell me if there is a better way of doing it?
Ok, the problem is more difficult than it appears, and a lot of code is required. Everything would have been simpler if you posted all of the necessary code to run it, that is a Minimal, Complete, and Verifiable Example. Anyway, I resorted to putting together a structure for the problem which allows to test it.
The piece which answers your question is the following one:
typedef struct board {
int side_;
char **dots_;
} board;
void board_set_possible_moves(board *b)
{
/* Directions
012
7 3
654 */
static int dr[8] = { -1,-1,-1, 0, 1, 1, 1, 0 };
static int dc[8] = { -1, 0, 1, 1, 1, 0,-1,-1 };
int side_ = b->side_;
char **dots_ = b->dots_;
for (int r = 0; r < side_; ++r) {
for (int c = 0; c < side_; ++c) {
// The place already has a dot
if (dots_[r][c] == 1)
continue;
// Count up to 4 dots in the 8 directions from current position
int ndots[8] = { 0 };
for (int d = 0; d < 8; ++d) {
for (int i = 1; i <= 4; ++i) {
int nr = r + dr[d] * i;
int nc = c + dc[d] * i;
if (nr < 0 || nc < 0 || nr >= side_ || nc >= side_ || dots_[nr][nc] != 1)
break;
++ndots[d];
}
}
// Decide if the position is a valid one
for (int d = 0; d < 4; ++d) {
if (ndots[d] + ndots[d + 4] >= 4)
dots_[r][c] = 2;
}
}
}
}
Note that I defined a square board with a pointer to pointers to chars, one per place. If there is a 0 in one of the places, then there is no dot and the place is not a valid move; if there is a 1, then there is a dot; if there is a 2, then the place has no dot, but it is a valid move. Valid here means that there are at least 4 dots aligned with the current one.
You can model the directions with a number from 0 to 7 (start from NW, move clockwise). Each direction has an associated movement expressed as dr and dc. Moving in every direction I count how many dots are there (up to 4, and stopping as soon as I find a non dot), and later I can sum opposite directions to obtain the total number of aligned points.
Of course these move are not necessarily valid, because we are missing the definition of lines already drawn and so we cannot check for them.
Here you can find a test for the function.
#include <stdio.h>
#include <stdlib.h>
board *board_init(board *b, int side) {
b->side_ = side;
b->dots_ = malloc(side * sizeof(char*));
b->dots_[0] = calloc(side*side, 1);
for (int r = 1; r < side; ++r) {
b->dots_[r] = b->dots_[r - 1] + side;
}
return b;
}
board *board_free(board *b) {
free(b->dots_[0]);
free(b->dots_);
return b;
}
void board_cross(board *b) {
board_init(b, 18);
for (int i = 0; i < 4; ++i) {
b->dots_[4][7 + i] = 1;
b->dots_[7][4 + i] = 1;
b->dots_[7][10 + i] = 1;
b->dots_[10][4 + i] = 1;
b->dots_[10][10 + i] = 1;
b->dots_[13][7 + i] = 1;
b->dots_[4 + i][7] = 1;
b->dots_[4 + i][10] = 1;
b->dots_[7 + i][4] = 1;
b->dots_[7 + i][13] = 1;
b->dots_[10 + i][7] = 1;
b->dots_[10 + i][10] = 1;
}
}
void board_print(const board *b, FILE *f)
{
int side_ = b->side_;
char **dots_ = b->dots_;
for (int r = 0; r < side_; ++r) {
for (int c = 0; c < side_; ++c) {
static char map[] = " oX";
fprintf(f, "%c%s", map[dots_[r][c]], c == side_ - 1 ? "" : " - ");
}
fprintf(f, "\n");
if (r < side_ - 1) {
for (int c = 0; c < side_; ++c) {
fprintf(f, "|%s", c == side_ - 1 ? "" : " ");
}
fprintf(f, "\n");
}
}
}
int main(void)
{
board b;
board_cross(&b);
board_set_possible_moves(&b);
board_print(&b, stdout);
board_free(&b);
return 0;
}
I am writing maze generator and at the some point I have to choose random unvisited neighbour of a cell. The first idea was just to enumerate neighbours such as left = 0, top = 1, right = 2, bottom = 3 and use rand() % 4 to generate random number and choose appropriate cell. However, not all cells features 4 neighbours, so that I had to write following code:
Cell* getRandomNeighbour(const Maze* const maze, const Cell* const currentCell) {
int randomNumb = rand() % 4;
int timer = 1;
while(timer > 0) {
if (randomNumb == 0 && currentCell->x < maze->width-1 && maze->map[currentCell->y][currentCell->x+1].isUnvisited)
return &maze->map[currentCell->y][currentCell->x+1];
if (randomNumb == 1 && currentCell->x > 0 && maze->map[currentCell->y][currentCell->x-1].isUnvisited)
return &maze->map[currentCell->y][currentCell->x-1];
if (randomNumb == 2 && currentCell->y < maze->height-1 && maze->map[currentCell->y+1][currentCell->x].isUnvisited)
return &maze->map[currentCell->y+1][currentCell->x];
if (randomNumb == 3 && currentCell->y > 0 && maze->map[currentCell->y-1][currentCell->x].isUnvisited)
return &maze->map[currentCell->y-1][currentCell->x];
timer--;
randomNumb = rand() % 4;
}
if (currentCell->x < maze->width-1 && maze->map[currentCell->y][currentCell->x+1].isUnvisited)
return &maze->map[currentCell->y][currentCell->x+1];
if (currentCell->x > 0 && maze->map[currentCell->y][currentCell->x-1].isUnvisited)
return &maze->map[currentCell->y][currentCell->x-1];
if (currentCell->y < maze->height-1 && maze->map[currentCell->y+1][currentCell->x].isUnvisited)
return &maze->map[currentCell->y+1][currentCell->x];
if (currentCell->y > 0 && maze->map[currentCell->y-1][currentCell->x].isUnvisited)
return &maze->map[currentCell->y-1][currentCell->x];
return NULL;
}
So, if after 10 iterations the right decision isn't chosen, it will be picked by brute force. This approach seems to be good for the reason that varying of variable timer changes the complexity of maze: the less timer is, the more straightforward maze is. Nevertheless, if my only purpose is to generate completely random maze, it takes a lot of execution time and look a little bit ugly. Is there any pattern(in C language) or way of refactoring that could enable me to deal with this situation without long switches and a lot of if-else constructions?
As #pat and #Ivan Gritsenko suggested, you can limit your random choice to the valid cells only, like this:
Cell* getRandomNeighbour(const Maze* const maze, const Cell* const currentCell)
{
Cell *neighbours[4] = {NULL};
int count = 0;
// first select the valid neighbours
if ( currentCell->x < maze->width - 1
&& maze->map[currentCell->y][currentCell->x + 1].isUnvisited ) {
neighbours[count++] = &maze->map[currentCell->y][currentCell->x + 1];
}
if ( currentCell->x > 0
&& maze->map[currentCell->y][currentCell->x - 1].isUnvisited ) {
neighbours[count++] = &maze->map[currentCell->y][currentCell->x - 1];
}
if ( currentCell->y < maze->height - 1
&& maze->map[currentCell->y + 1][currentCell->x].isUnvisited ) {
neighbours[count++] = &maze->map[currentCell->y + 1][currentCell->x];
}
if ( currentCell->y > 0
&& maze->map[currentCell->y - 1][currentCell->x].isUnvisited ) {
neighbours[count++] = &maze->map[currentCell->y - 1][currentCell->x];
}
// then choose one of them (if any)
int chosen = 0;
if ( count > 1 )
{
int divisor = RAND_MAX / count;
do {
chosen = rand() / divisor;
} while (chosen >= count);
}
return neighbours[chosen];
}
The rationale behind the random number generation part (as opposed to the more common rand() % count) is well explained in this answer.
Factoring repeated code, and a more disciplined way of picking the order of directions to try yields this:
// in_maze returns whether x, y is a valid maze coodinate.
int in_maze(const Maze* const maze, int x, int y) {
return 0 <= x && x < maze->width && 0 <= y && y < maze->height;
}
Cell *get_random_neighbour(const Maze* const maze, const Cell* const c) {
int dirs[] = {0, 1, 2, 3};
// Randomly shuffle dirs.
for (int i = 0; i < 4; i++) {
int r = i + rand() % (4 - i);
int t = dirs[i];
dirs[i] = dirs[r];
dirs[r] = t;
}
// Iterate through the shuffled dirs, returning the first one that's valid.
for (int trial=0; trial<4; trial++) {
int dx = (dirs[trial] == 0) - (dirs[trial] == 2);
int dy = (dirs[trial] == 1) - (dirs[trial] == 3);
if (in_maze(maze, c->x + dx, c->y + dy)) {
const Cell * const ret = &maze->map[c->y + dy][c->x + dx];
if (ret->isUnvisited) return ret;
}
}
return NULL;
}
(Disclaimer: untested -- it probably has a few minor issues, for example const correctness).
I have a simple (brute-force) recursive solver algorithm that takes lots of time for bigger values of OpxCnt variable. For small values of OpxCnt, no problem, works like a charm. The algorithm gets very slow as the OpxCnt variable gets bigger. This is to be expected but any optimization or a different algorithm ?
My final goal is that :: I want to read all the True values in the map array by
executing some number of read operations that have the minimum operation
cost. This is not the same as minimum number of read operations.
At function completion, There should be no True value unread.
map array is populated by some external function, any member may be 1 or 0.
For example ::
map[4] = 1;
map[8] = 1;
1 read operation having Adr=4,Cnt=5 has the lowest cost (35)
whereas
2 read operations having Adr=4,Cnt=1 & Adr=8,Cnt=1 costs (27+27=54)
#include <string.h>
typedef unsigned int Ui32;
#define cntof(x) (sizeof(x) / sizeof((x)[0]))
#define ZERO(x) do{memset(&(x), 0, sizeof(x));}while(0)
typedef struct _S_MB_oper{
Ui32 Adr;
Ui32 Cnt;
}S_MB_oper;
typedef struct _S_MB_code{
Ui32 OpxCnt;
S_MB_oper OpxLst[20];
Ui32 OpxPay;
}S_MB_code;
char map[65536] = {0};
static int opx_ListOkey(S_MB_code *px_kod, char *pi_map)
{
int cost = 0;
char map[65536];
memcpy(map, pi_map, sizeof(map));
for(Ui32 o = 0; o < px_kod->OpxCnt; o++)
{
for(Ui32 i = 0; i < px_kod->OpxLst[o].Cnt; i++)
{
Ui32 adr = px_kod->OpxLst[o].Adr + i;
// ...
if(adr < cntof(map)){map[adr] = 0x0;}
}
}
for(Ui32 i = 0; i < cntof(map); i++)
{
if(map[i] > 0x0){return -1;}
}
// calculate COST...
for(Ui32 o = 0; o < px_kod->OpxCnt; o++)
{
cost += 12;
cost += 13;
cost += (2 * px_kod->OpxLst[o].Cnt);
}
px_kod->OpxPay = (Ui32)cost; return cost;
}
static int opx_FindNext(char *map, int pi_idx)
{
int i;
if(pi_idx < 0){pi_idx = 0;}
for(i = pi_idx; i < 65536; i++)
{
if(map[i] > 0x0){return i;}
}
return -1;
}
static int opx_FindZero(char *map, int pi_idx)
{
int i;
if(pi_idx < 0){pi_idx = 0;}
for(i = pi_idx; i < 65536; i++)
{
if(map[i] < 0x1){return i;}
}
return -1;
}
static int opx_Resolver(S_MB_code *po_bst, S_MB_code *px_wrk, char *pi_map, Ui32 *px_idx, int _min, int _max)
{
int pay, kmax, kmin = 1;
if(*px_idx >= px_wrk->OpxCnt)
{
return opx_ListOkey(px_wrk, pi_map);
}
_min = opx_FindNext(pi_map, _min);
// ...
if(_min < 0){return -1;}
kmax = (_max - _min) + 1;
// must be less than 127 !
if(kmax > 127){kmax = 127;}
// is this recursion the last one ?
if(*px_idx >= (px_wrk->OpxCnt - 1))
{
kmin = kmax;
}
else
{
int zero = opx_FindZero(pi_map, _min);
// ...
if(zero > 0)
{
kmin = zero - _min;
// enforce kmax limit !?
if(kmin > kmax){kmin = kmax;}
}
}
for(int _cnt = kmin; _cnt <= kmax; _cnt++)
{
px_wrk->OpxLst[*px_idx].Adr = (Ui32)_min;
px_wrk->OpxLst[*px_idx].Cnt = (Ui32)_cnt;
(*px_idx)++;
pay = opx_Resolver(po_bst, px_wrk, pi_map, px_idx, (_min + _cnt), _max);
(*px_idx)--;
if(pay > 0)
{
if((Ui32)pay < po_bst->OpxPay)
{
memcpy(po_bst, px_wrk, sizeof(*po_bst));
}
}
}
return (int)po_bst->OpxPay;
}
int main()
{
int _max = -1, _cnt = 0;
S_MB_code best = {0};
S_MB_code work = {0};
// SOME TEST DATA...
map[ 4] = 1;
map[ 8] = 1;
/*
map[64] = 1;
map[72] = 1;
map[80] = 1;
map[88] = 1;
map[96] = 1;
*/
// SOME TEST DATA...
for(int i = 0; i < cntof(map); i++)
{
if(map[i] > 0)
{
_max = i; _cnt++;
}
}
// num of Opx can be as much as num of individual bit(s).
if(_cnt > cntof(work.OpxLst)){_cnt = cntof(work.OpxLst);}
best.OpxPay = 1000000000L; // invalid great number...
for(int opx_cnt = 1; opx_cnt <= _cnt; opx_cnt++)
{
int rv;
Ui32 x = 0;
ZERO(work); work.OpxCnt = (Ui32)opx_cnt;
rv = opx_Resolver(&best, &work, map, &x, -42, _max);
}
return 0;
}
You can use dynamic programming to calculate the lowest cost that covers the first i true values in map[]. Call this f(i). As I'll explain, you can calculate f(i) by looking at all f(j) for j < i, so this will take time quadratic in the number of true values -- much better than exponential. The final answer you're looking for will be f(n), where n is the number of true values in map[].
A first step is to preprocess map[] into a list of the positions of true values. (It's possible to do DP on the raw map[] array, but this will be slower if true values are sparse, and cannot be faster.)
int pos[65536]; // Every position *could* be true
int nTrue = 0;
void getPosList() {
for (int i = 0; i < 65536; ++i) {
if (map[i]) pos[nTrue++] = i;
}
}
When we're looking at the subproblem on just the first i true values, what we know is that the ith true value must be covered by a read that ends at i. This block could start at any position j <= i; we don't know, so we have to test all i of them and pick the best. The key property (Optimal Substructure) that enables DP here is that in any optimal solution to the i-sized subproblem, if the read that covers the ith true value starts at the jth true value, then the preceding j-1 true values must be covered by an optimal solution to the (j-1)-sized subproblem.
So: f(i) = min(f(j) + score(pos(j+1), pos(i)), with the minimum taken over all 1 <= j < i. pos(k) refers to the position of the kth true value in map[], and score(x, y) is the score of a read from position x to position y, inclusive.
int scores[65537]; // We effectively start indexing at 1
scores[0] = 0; // Covering the first 0 true values requires 0 cost
// Calculate the minimum score that could allow the first i > 0 true values
// to be read, and store it in scores[i].
// We can assume that all lower values have already been calculated.
void calcF(int i) {
int bestStart, bestScore = INT_MAX;
for (int j = 0; j < i; ++j) { // Always executes at least once
int attemptScore = scores[j] + score(pos[j + 1], pos[i]);
if (attemptScore < bestScore) {
bestStart = j + 1;
bestScore = attemptScore;
}
}
scores[i] = bestScore;
}
int score(int i, int j) {
return 25 + 2 * (j + 1 - i);
}
int main(int argc, char **argv) {
// Set up map[] however you want
getPosList();
for (int i = 1; i <= nTrue; ++i) {
calcF(i);
}
printf("Optimal solution has cost %d.\n", scores[nTrue]);
return 0;
}
Extracting a Solution from Scores
Using this scheme, you can calculate the score of an optimal solution: it's simply f(n), where n is the number of true values in map[]. In order to actually construct the solution, you need to read back through the table of f() scores to infer which choice was made:
void printSolution() {
int i = nTrue;
while (i) {
for (int j = 0; j < i; ++j) {
if (scores[i] == scores[j] + score(pos[j + 1], pos[i])) {
// We know that a read can be made from pos[j + 1] to pos[i] in
// an optimal solution, so let's make it.
printf("Read from %d to %d for cost %d.\n", pos[j + 1], pos[i], score(pos[j + 1], pos[i]));
i = j;
break;
}
}
}
}
There may be several possible choices, but all of them will produce optimal solutions.
Further Speedups
The solution above will work for an arbitrary scoring function. Because your scoring function has a simple structure, it may be that even faster algorithms can be developed.
For example, we can prove that there is a gap width above which it is always beneficial to break a single read into two reads. Suppose we have a read from position x-a to x, and another read from position y to y+b, with y > x. The combined costs of these two separate reads are 25 + 2 * (a + 1) + 25 + 2 * (b + 1) = 54 + 2 * (a + b). A single read stretching from x-a to y+b would cost 25 + 2 * (y + b - x + a + 1) = 27 + 2 * (a + b) + 2 * (y - x). Therefore the single read costs 27 - 2 * (y - x) less. If y - x > 13, this difference goes below zero: in other words, it can never be optimal to include a single read that spans a gap of 12 or more.
To make use of this property, inside calcF(), final reads could be tried in decreasing order of start-position (i.e. in increasing order of width), and the inner loop stopped as soon as any gap width exceeds 12. Because that read and all subsequent wider reads tried would contain this too-large gap and therefore be suboptimal, they need not be tried.
It is an interesting puzzle I came across , according to which , given an array , we need to find the ninja index in it.
A Ninja index is defined by these rules :
An index K such that all elements with smaller indexes have values lower or equal to A[K] and all elements with greater indexes have values greater or equal to A[K].
For example , consider :
A[0]=4, A[1]=2, A[2]=2, A[3]=3, A[4]=1, A[5]=4, A[6]=7, A[7]=8, A[8]=6, A[9]=9.
In this case, 5 is a ninja index , since A[r]<=A[5] for r = [0,k] and A[5]<=A[r] r = [k,n].
What algorithm shall we follow to find it in O(n) . I already have a brute force O(n^2) solution.
EDIT : There can be more than 1 ninja index , but we need to find the first one preferably. And in case there is no NI , then we shall return -1.
Precompute minimum values for all the suffixes of the array and maximum values for all prefixes. With this data every element can be checked for Ninja in O(1).
A python solution that will take O(3n) operations
def n_index1(a):
max_i = []
maxx = a[0]
for j in range(len(a)):
i=a[j]
if maxx<=i and j!=0:
maxx=i
max_i.append(1)
else:
max_i.append(-1)
return max_i
def n_index2(a):
max_i = []
maxx = -a[len(a)-1]
for j in range(len(a)-1,-1,-1):
i=-a[j] # mind the minus
if maxx<=i and j!=len(a)-1:
maxx=i
max_i.append(1)
else:
max_i.append(-1)
return max_i
def parse_both(a,b):
for i in range(len(a)):
if a[i]==1 and b[len(b)-1-i]==1:
return i
return -1
def ninja_index(v):
a = n_index1(v)
b = n_index2(v)
return parse_both(a,b)
Another Python solution, following the same general approach. Maybe a bit shorter.
def ninja(lst):
maxs = lst[::]
mins = lst[::]
for i in range(1, len(lst)):
maxs[ i] = max(maxs[ i], maxs[ i-1])
mins[-1-i] = min(mins[-1-i], mins[-i ])
return [i for i in range(len(lst)) if maxs[i] <= lst[i] <= mins[i]]
I guess it could be optimized a bit w.r.t that list-copying-action, but this way it's more concise.
This straight-forward Java code calculates leftmost index that has property "all elements rightwards are not lesser":
private static int fwd(int[] a) {
int i = -1;
for (int j = 0; j < a.length - 1; j++) {
if (a[j + 1] >= a[j] && i == -1) {
i = j + 1;
} else if (i != -1 && a[j + 1] < a[i]) {
i = -1;
}
}
return i;
}
Almost same code calculates leftmost index that has property "all elements leftwards are not greater":
private static int bwd(int[] a) {
int i = -1;
for (int j = 0; j < a.length - 1; j++) {
if (a[j + 1] >= a[j] && i == -1) {
i = j + 1;
} else if (i != -1 && a[j + 1] < a[i]) {
i = -1;
}
}
return i;
}
If results are the same, leftmost Ninja index is found.
This is a trivial algorithmic question, I believe, but I don't seem to be able to find an efficient and elegant solution.
We have 3 arrays of int (Aa, Ab, Ac) and 3 cursors (Ca, Cb, Cc) that indicate an index in the corresponding array. I want to identify and increment the cursor pointing to the smallest value. If this cursor is already at the end of the array, I will exclude it and increment the cursor pointing to the second smallest value. If there is only 1 cursor that is not at the end of the array, we increment this one.
The only solutions I can come up are complicated and/or not optimal. For example, I always end up with a huge if...else...
Does anyone see a neat solution to this problem ?
I am programming in C++ but feel free to discuss it in pseudo-code or any language you like.
Thank you
Pseudo-java code:
int[] values = new int[3];
values[0] = aa[ca];
values[1] = ab[cb];
values[2] = ac[cc];
Arrays.sort(values);
boolean done = false;
for (int i = 0; i < 3 && !done; i++) {
if (values[i] == aa[ca] && ca + 1 < aa.length) {
ca++;
done = true;
}
else if (values[i] == ab[cb] && cb + 1 < ab.length) {
cb++;
done = true;
}
else if (cc + 1 < ac.length) {
cc++;
done = true;
}
}
if (!done) {
System.out.println("cannot increment any index");
stop = true;
}
Essentially, it does the following:
initialize an array values with aa[ca], ab[cb] and ac[cc]
sort values
scan values and increment if possible (i.e. not already at the end of the array) the index of the corresponding value
I know, sorting is at best O(n lg n), but I'm only sorting an array of 3 elements.
what about this solution:
if (Ca != arraySize - 1) AND
((Aa[Ca] == min(Aa[Ca], Ab[Cb], Ac[Cc]) OR
(Aa[Ca] == min(Aa[Ca], Ab[Cb]) And Cc == arraySize - 1) OR
(Aa[Ca] == min(Aa[Ca], Ac[Cc]) And Cb == arraySize - 1) OR
(Cc == arraySize - 1 And Cb == arraySize - 1))
{
Ca++;
}
else if (Cb != arraySize - 1) AND
((Ab[Cb] == min(Ab[Cb], Ac[Cc]) OR (Cc == arraySize - 1))
{
Cb++;
}
else if (Cc != arraySize - 1)
{
Cc++;
}
Pseudo code: EDIT : tidied it up a bit
class CursoredArray
{
int index;
std::vector<int> array;
int val()
{
return array[index];
}
bool moveNext()
{
bool ret = true;
if( array.size() > index )
++index;
else
ret = false;
return ret;
}
}
std::vector<CursoredArray> arrays;
std::vector<int> order = { 0, 1, 2 };//have a default order to start with
if( arrays[0].val() > arrays[1].val() )
std::swap( order[0], order [1] );
if( arrays[2].val() < arrays[order[1]].val() )//if the third is less than the largest of the others
{
std::swap( order[1], order [2] );
if( arrays[2].val() < arrays[order[0]].val() )//if the third is less than the smallest of the others
std::swap( order[0], order [1] );
}
//else third pos of order is already correct
bool end = true;
for( i = 0; i < 3; ++i )
{
if( arrays[order[i]].MoveNext() )
{
end = false;
break;
}
}
if( end )//have gone through all the arrays