Algorithmic solution of card puzzle - algorithm

Given is a puzzle game with nine square cards.
On each of the cards there are 4 pictures at top, right, bottom and left.
Each picture on a card depicts either the front part or the rear part of an animal (a crocodile). Each picture has one of 5 colors.
Goal: to lay out the nine cards in a 3x3 grid in such a way that all "inner" (complete) crocodiles are properly combined with adjacent cards, i.e. have a front and rear end as well as matching colors.
To get a visual grip on the problem, here is a picture of the puzzle:
I found the depicted solution by hand.
Even though the puzzle looks simple at first glance, there is an extremely big number of combinations given that you can rotate each piece in 4 different ways.
The problem is now that I'd like to have an algorithm generating all possible 3x3 layouts in order to check all possible solutions (if there are any others). Preferably in Processing/Java.
Thoughts so far:
My approach would be to represent each of the 9 pieces by an array of 4 integer numbers, representing the 4 rotational states of a piece. Then generate all possible permutations of these 9 pieces, picking 1 of the 4 rotation-states from a piece array. A function isValidSolution() could then check a solution for violation of the constraints (color matching and front-rear matching).
Any ideas on how to implement this?

It is possible to find all the solutions, trying not to explore all the unsuccessful paths of the search tree. The C++ code below, not highly optimized, finds a total of 2 solutions (that turn out to be the same unique solution because there is a duplicated tile, right answer?) almost instantaneously with my computer.
The trick here to avoid exploring all the possibilities is to call to function isValidSolution() while we are still placing the tiles (the function handles empty tiles). Also, to speed up the process, I follow a given order placing the tiles, starting in the middle, then the cross around it at left, right, top and bottom, and then the corners top-left, top-right, bottom-left and bottom-right. Probably other combinations give quicker executions.
It is of course possible to optimize this because of the special pattern distribution in this puzzle (the pattern with the letters only accepts one possible match), but that's beyond the scope of my answer.
#include<iostream>
// possible pattern pairs (head, body)
#define PINK 1
#define YELLOW 2
#define BLUE 3
#define GREEN 4
#define LACOSTE 5
typedef int8_t pattern_t; // a pattern is a possible color, positive for head, and negative for body
typedef struct {
pattern_t p[4]; // four patterns per piece: top, right, bottom, left
} piece_t;
unsigned long long int solutionsCounter = 0;
piece_t emptyPiece = {.p = {0, 0, 0, 0} };
piece_t board[3][3] = {
{ emptyPiece, emptyPiece, emptyPiece},
{ emptyPiece, emptyPiece, emptyPiece},
{ emptyPiece, emptyPiece, emptyPiece},
};
inline bool isEmpty(const piece_t& piece) {
bool result = (piece.p[0] == 0);
return result;
}
// check current solution
bool isValidSolution() {
int i, j;
for (i = 0; i < 2; i++) {
for (j = 0; j < 3; j++) {
if (!isEmpty(board[i][j]) && !isEmpty(board[i+1][j]) && (board[i][j].p[1] != -board[i+1][j].p[3])) {
return false;
}
}
}
for (i = 0; i < 3; i++) {
for (j = 0; j < 2; j++) {
if (!isEmpty(board[i][j]) && !isEmpty(board[i][j+1]) && (board[i][j].p[2] != -board[i][j+1].p[0])) {
return false;
}
}
}
return true;
}
// rotate piece
void rotatePiece(piece_t& piece) {
pattern_t paux = piece.p[0];
piece.p[0] = piece.p[1];
piece.p[1] = piece.p[2];
piece.p[2] = piece.p[3];
piece.p[3] = paux;
}
void printSolution() {
printf("Solution:\n");
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
printf("\t %2i ", (int) board[j][i].p[0]);
}
printf("\n");
for (int j = 0; j < 3; j++) {
printf("\t%2i %2i", (int) board[j][i].p[3], (int) board[j][i].p[1]);
}
printf("\n");
for (int j = 0; j < 3; j++) {
printf("\t %2i ", (int) board[j][i].p[2]);
}
printf("\n");
}
printf("\n");
}
bool usedPiece[9] = { false, false, false, false, false, false, false, false, false };
int colocationOrder[9] = { 4, 3, 5, 1, 7, 0, 2, 6, 8 };
void putNextPiece(piece_t pieces[9], int pieceNumber) {
if (pieceNumber == 9) {
if (isValidSolution()) {
solutionsCounter++;
printSolution();
}
} else {
int nextPosition = colocationOrder[pieceNumber];
int maxRotations = (pieceNumber == 0) ? 1 : 4; // avoids rotation symmetries.
for (int pieceIndex = 0; pieceIndex < 9; pieceIndex++) {
if (!usedPiece[pieceIndex]) {
usedPiece[pieceIndex] = true;
for (int rotationIndex = 0; rotationIndex < maxRotations; rotationIndex++) {
((piece_t*) board)[nextPosition] = pieces[pieceIndex];
if (isValidSolution()) {
putNextPiece(pieces, pieceNumber + 1);
}
rotatePiece(pieces[pieceIndex]);
}
usedPiece[pieceIndex] = false;
((piece_t*) board)[nextPosition] = emptyPiece;
}
}
}
}
int main() {
// register all the pieces (already solved, scramble!)
piece_t pieces[9] = {
{.p = { -YELLOW, -BLUE, +GREEN, +PINK} },
{.p = { -YELLOW, -GREEN, +PINK, +BLUE} },
{.p = { -BLUE, -YELLOW, +PINK, +GREEN }},
{.p = { -GREEN, -BLUE, +PINK, +YELLOW }},
{.p = { -PINK, -LACOSTE, +GREEN, +BLUE }},
{.p = { -PINK, -BLUE, +GREEN, +LACOSTE }},
{.p = { -PINK, -BLUE, +PINK, +YELLOW }},
{.p = { -GREEN, -YELLOW, +GREEN, +BLUE }},
{.p = { -GREEN, -BLUE, +PINK, +YELLOW }}
};
putNextPiece(pieces, 0);
printf("found %llu solutions\n", solutionsCounter);
return 0;
}

There are only 9 pieces, and thus each potential solution is representable by a small structure (say a 3x3 array of pieces, each piece with it's rotation), so the exact description of the pieces isn't too important.
Trying all the possible permutations is wasteful (to abuse LaTeX here, to place the 9 pieces on the grid can be done in $9!$ orders, as each one can be in 4 different orientations this gives a total of $9! \cdot 4^9 = 95126814720 \approx 10^{11}$, a bit too much to check them all). What you'd do by hand is to place a piece, say at the upper left side, and try to complete the square by fitting matching pieces into the rest. So you'd never consider any combinations where the first and second pieces don't match, cutting the search down considerably. This kind of idea is called backtracking. For it you need a description of the partial solution (the 3x3 grid with the filled in pieces and blank places, and the pieces not yet used; a specific order in which to fill the grid), a way of moving forward (place next piece if it fits, skip that one if it doesn't) and backwards (can't find any fits, undo last move and try the next possibility).
Obviously you have to design a way to find out if a potential match exists (given the filled in neighbors, try all orientations of a piece in it's asigned place). For such a small problem this probably isn't performance critical, but if you'd try to solve, say 100x100 the case is different...

Related

Gin Rummy - Algorithm for determining optimal melding

A similar question to this has been asked here, but in my question, rather than being restricted to melds of size 3, melds can be any size.
In Gin Rummy, for any particular set of cards, cards can be grouped into either sets or runs. A set is a group of 3 or more cards that are all of the same rank (2-D, 2-C, 2-H or 7-D, 7-C, 7-H, 7-S). A run is a group of 3 or more cards with consecutive ranks and identical suits (A-D, 2-D, 3-C or 7-C, 8-C, 9-C, 10-C, J-C). Cards not belonging to a group are called "deadwood".
The goal of my algorithm is to find the optimal melding for a particular set of cards, which is one that minimizes the sum of the values of all the deadwood (The values of number cards are their associated numbers, the value of the ace is 1, and the value of face cards is 10.).
My original attempt at an algorithm worked on the assumption that for any run and group of sets that conflicted, either the run would exist or the group of sets would exist. Under this assumption, the algorithm could just calculate the sum of the values of the run and the sum of the values of all the sets, and keep whichever was greater. For example, if we had the groups
[2-D, 3-D, 4-D], [2-D, 2-C, 2-H], and [4-D, 4-H, 4-S]. The sum of the run's value would be 2 + 3 + 4 = 9, and the sum of the all the set's values would be 2 + 2 + 2 + 4 + 4 + 4 = 18. In this case, this would mean the two sets would be kept for the optimal melding and the run would not be used (3-D would be deadwood).
This assumption worked for groups of size 3, but fails with larger groups. For example, consider the following two groups:
[4-D, 5-D, 6-D, 7-D], [7-D, 7-H, 7-S]
The optimal grouping for this ends up being [4-D, 5-D, 6-D] and [7-D, 7-H, 7-S]. The conflicting set and part of the run is kept. I'm not sure how to create an algorithm, that isn't just brute force.
Any help or ideas would be appreciated.
EDIT
I'm realizing that my original algorithm doesn't even work for size 3 melds. In the case of the following groups:
[4-D, 5-D, 6-D], [4-C, 5-C, 6-C], [6-D, 6-C, 6-S]
The algorithm would look at the two runs individually, and conclude that they should be removed in favor of the set, but the optimal solution would be to keep both runs and remove the set.
Still looking for help in creating an algorithm that works in all edge cases.
My previous answer got deleted as I didn't really provide an explanation, and simply provided a link to a script with an algorithm for this problem. I realize why that isn't fit for an answer on stack overflow now. Here's my attempt at a complete answer.
The algorithm I show here is based on the approach found here: https://gist.github.com/yalue/2622575. The solution is a backtracking search as Paul Hankin suggested above.
The algorithm creates an object called MeldNode:
class MeldNode {
Cards cards;
MeldNode* parent;
double value;
MeldNode(Cards cards, MeldNode* parent) : cards(cards), parent(parent) {
value = sum of values of cards;
if (parent is not null){
value += parent->value;
}
}
}
Here value is equal to the sum of the card values of the cards provided and the value of the parent.
A function cleanMeldGroup is created which, given an array of melds and a meld, will return an array of melds with only melds that don't conflict with the given meld.
Melds cleanMeldGroup(Melds melds, Cards meldAvoid) {
Melds cleanMelds;
for (Cards meld : melds) {
bool clean = true;
for (Card cardA : meld) {
for (Card cardB : meldAvoid) {
if (cardA == cardB) {
clean = false;
}
}
}
if (clean) {
cleanMelds.push(meld);
}
}
return cleanMelds;
}
Next, a function getBestNode is created, which uses backtracking to find a meld node containing the data for the optimal melding combination.
MeldNode* getBestNode(Melds melds, MeldNode* rootNode) {
MeldNode* best = rootNode;
for (Meld meld : melds) {
MeldNode* node = new MeldNode(meld, rootNode);
MeldNode* newTree = getBestNode(cleanMeldGroup(melds, meld), node);
if (best is null || newTree->value > best->value){
best = newTree;
}
}
}
Note that as this is written now, this would result in memory leaks in c++. If necessary, take measures to free memory when the data has been used (You could free the memory of Nodes that aren't part of the best tree in this function, and then free the memory of Nodes that are part of the best tree after you use them).
Finally, the optimal melding can be determined as follows using the getOptimalMelding function.
Melds getOptimalMelding(Cards hand) {
Sort hand by rank and then by suit;
Melds possibleMelds;
// Find all possible runs
int runLength = 1;
for (int i = 0; i < hand.size(); i++) {
if (hand[i].suit == hand[i - 1].suit && hand[i].rank == hand[i - 1].rank + 1) {
runLength++;
} else {
if (runLength >= 3) {
for (int size = 3; size <= runLength; size++) {
for (int start = 0; start <= runLength - size; start++) {
Cards run;
for (int j = i - runLength + start; j < i - runLength + start + s; j++) {
run.push(hand[j]);
}
possibleMelds.push(run);
}
}
}
runLength = 1;
}
}
if (runLength >= 3) {
for (int size = 3; size <= runLength; size++) {
for (int start = 0; start <= runLength - size; start++) {
Cards run;
for (int j = i - runLength + start; j < i - runLength + start + s; j++) {
run.push(hand[j]);
}
possibleMelds.push(run);
}
}
}
// Find all possible sets
for (int i = 1; i <= 13; i++) {
Cards set;
for (Card card : hand) {
if (card.rank == i) {
set.push(card);
}
}
if (set.size() >= 3) {
possibleMelds.push(set);
}
if (set.size() == 4) {
for (Card card : set) {
Cards subset;
for (Card add : set) {
if (add != card) {
subset.push(add);
}
}
possibleMelds.push(subset);
}
}
}
// Find Optimal Melding Combination
MeldNode* bestNode = getBestNode(possibleMelds, null);
Melds optimalMelds;
while (bestNode is not null){
optimalMelds.push(bestNode.cards);
bestNode = bestNode->parent;
}
return optimalMelds;
Note that possibleMelds contains all possible melds of all sizes. For example, for the hand [2-D, 3-D, 4-D, 5-D, 5-H, 5-S, 5-C, 10-S, 9-C, 8-H], possibleMelds would contain the following groups:
[2-D, 3-D, 4-D],
[3-D, 4-D, 5-D],
[2-D, 3-D, 4-D, 5-D],
[5-D, 5-H, 5-S, 5-C],
[5-H, 5-S, 5-C],
[5-D, 5-S, 5-C],
[5-D, 5-H, 5-C],
[5-D, 5-H, 5-S]

Algorithm: use union find to count number of islands

Suppose you need to count the number of islands on a matrix
{1, 1, 0, 0, 0},
{0, 1, 0, 0, 1},
{1, 0, 0, 1, 1},
{0, 0, 0, 0, 0},
{1, 0, 1, 0, 1}
We could simply use DFS or BFS when the input matrix size can be fitting into the memory.
However, what do we do if the input matrix is really large which could not be fitting into the memory?
I could chunk/split the input matrix into different small files and read them respectively.
But how to merge them?
I got stuck at how to merge them. I have the idea that when merging them we have to read some overlapped portion. But what is a concrete way to do so?
Trying to understand Matt's solution.
When I drew the below sample on the whiteboard and process it row by row.
Merge left then merge top and it seems won't work.
From Matt's solution.
not sure what are topidx, botidx meaning
int topidx = col * 2;
int botidx = topidx + 1;
Using union-find, the basic algorithm (without worrying about memory) is:
Create a set for every 1
Merge the sets for every pair of adjacent 1s. It doesn't matter what order you find them in, so reading order is usually fine.
Count the number of root sets -- there will be one for every island.
Easy, and with a little care, you can do this using sequential access to the matrix and only 2 rows worth of memory:
Initialize the island count to 0
Read the first row, create a set for each 1, and merge sets in adjacent columns.
For each additional row:
Read the row, create a set for each 1, and merge sets in adjacent columns;
Merge sets in the new row with adjacent sets in the previous row. ALWAYS POINT THE LINKS DOWNWARD, so that you never end up with a set in the new row linked to a parent in the old row.
Count the remaining root sets in the previous row, and add the number to your island count. These will never be able to merge with anything else.
Discard all the sets in the previous row -- you're never going to need them again, because you already counted them and nothing links to them.
Finally, count the root sets in the last row and add them to your island count.
The key to this, of course, is always pointing the links downward whenever you link sets in different rows. This will not hurt the complexity of the algorithm, and if you're using your own union-find, then it is easy to accomplish. If you're using a library data structure then you can use it just for each row, and keep track of the links between root sets in different rows yourself.
Since this is actually one of my favorite algorithms, here is an implementation in Java. This is not the most readable implementation since it involves some low-level tricks, but is super-efficient and short -- the kind of thing I'd write where performance is very important:
import java.util.Arrays;
public class Islands
{
private static final String[] matrix=new String[] {
" ############# ### ",
" # ##### ## ",
" # ## ## # # ",
" ### ## # # ",
" # ######### ## ## ",
" ## ## ",
" ########## ",
};
// find with path compression.
// If sets[s] < 0 then it is a link to ~sets[s]. Otherwise it is size of set
static int find(int[] sets, int s)
{
int parent = ~sets[s];
if (parent>=0)
{
int root = find(sets, parent);
if (root != parent)
{
sets[s] = ~root;
}
return root;
}
return s;
}
// union-by-size
// If sets[s] < 0 then it is a link to ~sets[s]. Otherwise it is size of set
static boolean union(int[] sets, int x, int y)
{
x = find(sets,x);
y = find(sets,y);
if (x!=y)
{
if ((sets[x] < sets[y]))
{
sets[y] += sets[x];
sets[x] = ~y;
}
else
{
sets[x] += sets[y];
sets[y] = ~x;
}
return true;
}
return false;
}
// Count islands in matrix
public static void main(String[] args)
{
// two rows of union-find sets.
// top row is at even indexes, bottom row is at odd indexes. This arrangemnt is chosen just
// to make resizing this array easier.
// For each value x:
// x==0 => no set. x>0 => root set of size x. x<0 => link to ~x
int cols=4;
int[] setrows= new int[cols*2];
int islandCount = 0;
for (String s : matrix)
{
System.out.println(s);
//Make sure our rows are big enough
if (s.length() > cols)
{
cols=s.length();
if (setrows.length < cols*2)
{
int newlen = Math.max(cols,setrows.length)*2;
setrows = Arrays.copyOf(setrows, newlen);
}
}
//Create sets for land in bottom row, merging left
for (int col=0; col<s.length(); ++col)
{
if (!Character.isWhitespace(s.charAt(col)))
{
int idx = col*2+1;
setrows[idx]=1; //set of size 1
if (idx>=2 && setrows[idx-2]!=0)
{
union(setrows, idx, idx-2);
}
}
}
//merge up
for (int col=0; col<cols; ++col)
{
int topidx = col*2;
int botidx = topidx+1;
if (setrows[topidx]!=0 && setrows[botidx]!=0)
{
int toproot=find(setrows,topidx);
if ((toproot&1)!=0)
{
//top set is already linked down
union(setrows, toproot, botidx);
}
else
{
//link top root down. It does not matter that we aren't counting its size, since
//we will shortly throw it aaway
setrows[toproot] = ~botidx;
}
}
}
//count root sets, discard top row, and move bottom row up while fixing links
for (int col=0; col<cols; ++col)
{
int topidx = col * 2;
int botidx = topidx + 1;
if (setrows[topidx]>0)
{
++islandCount;
}
int v = setrows[botidx];
setrows[topidx] = (v>=0 ? v : v|1); //fix up link if necessary
setrows[botidx] = 0;
}
}
//count remaining root sets in top row
for (int col=0; col<cols; ++col)
{
if (setrows[col*2]>0)
{
++islandCount;
}
}
System.out.println("\nThere are "+islandCount+" islands there");
}
}

Find the maximum height they can make by standing on each other?

Weights of n men and their strengths (max weight they can carry) are given. Height of all are same and given. Find the maximum height they can make by standing on each other?
That means, you have to place them by taking maximum number of men from them, such that no men is carrying weight more than his strength.
This question is bugging me. First I thought using greedy, by taking person of maximum strength first, but it is not giving correct answer. Then I tried to solve it, like knapsack, which is also not right. I am not able to come up with an efficient algorithm. Can anyone help?
First of all sorry by my english :)
Here is one way that you can think as a way to solve the problem.
Ok if you can supposed that each floor absorbs the whole weight in a uniform form, ( I mean there are no restriction like "one man can carry only the weight of two mens" or somethin like that..).
We will start with an hypothetical structure which has one man for each floor, and with that structure we will start to check the restrictions and arrange people.
We will check the lowest floor (first floor), and we will ask: Can this floor handle the weight of all the higher floors?
If the answer is no, we remove one men from the top of the tower and we add it to this floor, and we check again the weight condition on this floor.
If the answer is yes, we pass to check the next floor.
After that we will have an structure which meet the requirements.
And the C# code:
int amountOfMens = n;
float weight = w;
float strength = s;
float height = h;
int []mensInEachFloor;
public void MyAlg()
{
mensInEachFloor = new int[ amountOfMens ]; // the max height that we can achieve is the max amount of mens.
for(int i=0; i < mensInEachFloor.Length; i++ )
{
// we put one men on each floor, just to check if the highest heigth is achivable
mensInEachFloor[i] = 1;
}
// now we start to use our algorithm
// for each floor:
for(int i = 0; i < mensInEachFloor.Length; i++ )
{
// for each floor we will work on it until supports its designed weight
bool floorOk = false;
while(! floorOk)
{
// we check if the weigth of all the higher floors can be supported by this level
float weightToBeSupported = TotalWeightOfHigherFloors(i+1);
float weightThatCanBeSupported = WeightHandledByFloor(i);
if( weightToBeSupported > weightThatCanBeSupported )
{
// Remove one men from the top
RemoveOneManFromHighestFloor();
// add one men to this floor to help with the weight
AddOneManToFloor(i);
}
else
{
// we are ok on this floor :)
floorOk = true;
}
}
}
Debug.Log("The total heigth of the tower is : " + GetTowerHeight() );
}
private float TotalWeightOfHigherFloors(int startingFloor)
{
float totalWeight = 0;
for(int i= startingFloor; i< mensInEachFloor.Length; i++ )
{
totalWeight += mensInEachFloor[i] * weight;
}
return totalWeight;
}
private float WeightHandledByFloor(int floor)
{
return mensInEachFloor[floor] * strength;
}
private void RemoveOneManFromHighestFloor()
{
// we start to see from the top..
for(int i = mensInEachFloor.Length - 1 ; i >= 0; i-- )
{
// if on this floor are one or more mens..
if(mensInEachFloor[i] != 0)
{
// we remove from the floor
mensInEachFloor[i] = mensInEachFloor[i] - 1;
// and we are done
break;
}
}
}
private void AddOneManToFloor(int floor)
{
// Add one man to the selected floor
mensInEachFloor[floor] = mensInEachFloor[floor] + 1;
}
private float GetTowerHeight()
{
// We will count the number of floors with mens on it
float amountOfFloors = 0;
for(int i= 0; i< mensInEachFloor.Length; i++ )
{
// If there are more than zero mens
if( mensInEachFloor[i] > 0 )
{
// it means that it is a valid floor
amountOfFloors++;
}
}
// number of floors times height
return amountOfFloors * height;
}
Cheers !

Running sum maximum item minimization

I am making a physics optimizer, where a critical part is solved by the following CS question.
You are given an array of random signed integers. Their sum is zero. One can make a loop that keeps a running sum as follows:
int running_sum = 0;
int sum_peak = 0;
for( int i = 0; i < size_of_array; i++ )
{
running_sum += int_array[i];
sum_peak = max( sum_peak, abs( running_sum ) );
}
assert( running_sum == 0 );
The task is to minimize the resultant sum_peak by permuting the initial int_array. My thoughts so far: the naive approach takes an exponential amount of time to run. This problem does not seem to be expressed by any NP-C problems I know of. I can't think of any way to represent this problem as a graph problem.
If X is the largest number (by absolute value) in the array, the upper and lower bounds of max_sum are N and N/2, respectively.
EDIT: examples.
{-4, -6, 10}: reorder the list as the following: {-6, 10, -4}, so that the sum_peak is -6.
{1, 1, 1, 1, -4}: reorder the list as the following: { 1, 1, -4, 1, 1 }, so that the sum_peak is +2.
this may be a bogus (if I misunderstood the problem) but sometimes the easiest way is the best one
if the int's are unsigned then there is no help
if there are signed then:
1.input array
1,5,-2,7,-6,4,-10,-3,...
2.separate signed and unsigned valueas and sort by value
+1,+4,+5,+7,...
-2,-3,-6,-10,...
3.reorder original array
this can be done more ways
the easiest one is just even numbers are + and odd ones are -
+1,-2,+4,-3,+5,-6,+7,-10
this is fast but not optimal
still will cut down the peak significantly
another option is:
1.while (sum>=0) add number from +array
2.while (sum< 0) add number from -array
3.when one array is all used then copy the unused rest
do not think you can do better then this
Where did the NP complete come from ???
or I am missing something ???
[edit 1]
also it can be improved to obtain optimal solution like
use smaller from the big unused values to get sum to half size of the max unused value from the opposite array...
also all can be done in-place
[edit 2] some code and testing
//---------------------------------------------------------------------------
void min_sum_peak()
{
const int N=1000;
int in[N],out[N];
int i,s,sp,e,ip0,im0,ip1,im1;
// generate sum=0 random array to in and clear out (for easyier debug)
for (s=0,i=1;i<N;i++)
{
in[i]=Random(1000)-500;
s+=in[i];
out[i]=0;
} in[0]=-s; out[0]=0;
// bubble sort in[]
for (e=1;e;)
for (e=0,i=1;i<N;i++)
if (in[i-1]>in[i])
{ e=in[i-1]; in[i-1]=in[i]; in[i]=e; e=1; }
// fill out[]
#define peak { e=s; if (e<0) e=-e; if (sp<e) sp=e; }
for (int mode=0;mode<3;mode++)
{
// scann for +/- boundary
im0=-1; ip0=N;
im1= 0; ip1=N-1;
for (i=0;i<N;i++)
if (in[i]<0) im0=i;
else { ip0=i; break; }
if (mode==0) // even odd from smaller values (sp = ~2*max)
for (i=0,s=0,sp=0;i<N;)
{
if (im0>=im1){ out[i]=in[im0]; s+=out[i]; im0--; i++; peak; }
if (ip0<=ip1){ out[i]=in[ip0]; s+=out[i]; ip0++; i++; peak; }
}
if (mode==1) // even odd from bigger values (sp = ~max)
for (i=0,s=0,sp=0;i<N;)
{
if (im0>=im1){ out[i]=in[im1]; s+=out[i]; im1++; i++; peak; }
if (ip0<=ip1){ out[i]=in[ip1]; s+=out[i]; ip1--; i++; peak; }
}
if (mode==2) // -half sum to next max value (sp = ~0.5*max for big enough array)
{
for (i=0,s=0,sp=0;;)
{
if (im0<im1) break; // stop if any + or - valueas are exhausted
if (ip0>ip1) break;
if (s>=0)
{
if (+s+s<-in[im1]){ out[i]=in[ip0]; s+=out[i]; ip0++; i++; }
else { out[i]=in[im1]; s+=out[i]; im1++; i++; }
}
else{
if (-s-s<+in[ip1]){ out[i]=in[im0]; s+=out[i]; im0--; i++; }
else { out[i]=in[ip1]; s+=out[i]; ip1--; i++; }
}
peak;
}
for (;im0>=im1;){ out[i]=in[im0]; s+=out[i]; im0--; i++; peak; }
for (;ip0<=ip1;){ out[i]=in[ip0]; s+=out[i]; ip0++; i++; peak; }
}
i=-in[0]; if (i<in[N-1]) i=in[N-1];
// breakpoint here for approach assesment
mode=mode; // used approach
i = i; // abs max value
s = s; // sum
sp=sp; // sum peak
}
#undef peak { e=s; if (e<0) e=-e; if (sp<e) sp=e; }
}
//---------------------------------------------------------------------------
mode 0 - even odd from smaller values sp = ~2*max_abs_value
mode 1 - even odd from bigger values sp = ~max_abs_value
mode 2 - half sum to next max value sp = ~0.5*max_abs_value
for big enough arrays with 'evenly' distributed +/- values is mode 2 the best as expected
but if array is small (for example N=20) then mode 1 is the winner
mode 0 is not good at all

ACM Problem: Coin-Flipping, help me identify the type of problem this is

I'm practicing for the upcoming ACM programming competition in a week and I've gotten stumped on this programming problem.
The problem is as follows:
You have a puzzle consisting of a square grid of size 4. Each grid square holds a single coin; each coin is showing either heads (H) and tails (T). One such puzzle is shown here:
H H H H
T T T T
H T H T
T T H T
Any coin that is current showing Tails (T) can be flipped to Heads (H). However, any time we flip a coin, we must also flip the adjacent coins direct above, below and to the left and right in the same row. Thus if we flip the second coin in the second row we must also flip 4 other coins, giving us this arrangment (coins that changed are shown in bold).
H T H H
H H H T
H H H T
T T H T
If a coin is at the edge of the puzzle, so there is no coin on one side or the other, then we flip fewer coins. We do not "wrap around" to the other side. For example, if we flipped the bottom right coin of the arragnement above we would get:
H T H H
H H H T
H H H H
T T T H
Note: Only coins showing (T) tails can be selected for flipping. However, anytime we flip such a coin, adjacent coins are also flipped, regardless of their state.
The goal of the puzzle is to have all coins show heads. While it is possible for some arragnements to not have solutions, all the problems given will have solutions. The answer we are looking for is, for any given 4x4 grid of coins what is the least number of flips in order to make the grid entirely heads.
For Example the grid:
H T H H
T T T H
H T H T
H H T T
The answer to this grid is: 2 flips.
What I have done so far:
I'm storing our grids as two-dimensional array of booleans. Heads = true, tails = false.
I have a flip(int row, int col) method that will flip the adjacent coins according the rules above and I have a isSolved() method that will determine if the puzzle is in a solved state (all heads). So we have our "mechanics" in place.
The part we are having problems with is how should we loop through, going an the least amount of times deep?
Your puzzle is a classic Breadth-First Search candidate. This is because you're looking for a solution with the fewest possible 'moves'.
If you knew the number of moves to the goal, then that would be ideal for a Depth-First Search.
Those Wikipedia articles contain plenty of information about the way the searches work, they even contain code samples in several languages.
Either search can be recursive, if you're sure you won't run out of stack space.
EDIT: I hadn't noticed that you can't use a coin as the primary move unless it's showing tails. That does indeed make order important. I'll leave this answer here, but look into writing another one as well.
No pseudo-code here, but think about this: can you ever imagine yourself flipping a coin twice? What would be the effect?
Alternative, write down some arbitrary board (literally, write it down). Set up some real world coins, and pick two arbitrary ones, X and Y. Do an "X flip", then a "Y flip" then another "X flip". Write down the result. Now reset the board to the starting version, and just do a "Y flip". Compare the results, and think about what's happened. Try it a few times, sometimes with X and Y close together, sometimes not. Become confident in your conclusion.
That line of thought should lead you to a way of determining a finite set of possible solutions. You can test all of them fairly easily.
Hope this hint wasn't too blatant - I'll keep an eye on this question to see if you need more help. It's a nice puzzle.
As for recursion: you could use recursion. Personally, I wouldn't in this case.
EDIT: Actually, on second thoughts I probably would use recursion. It could make life a lot simpler.
Okay, perhaps that wasn't obvious enough. Let's label the coins A-P, like this:
ABCD
EFGH
IJKL
MNOP
Flipping F will always involve the following coins changing state: BEFGJ.
Flipping J will always involve the following coins changing state: FIJKN.
What happens if you flip a coin twice? The two flips cancel each other out, no matter what other flips occur.
In other words, flipping F and then J is the same as flipping J and then F. Flipping F and then J and then F again is the same as just flipping J to start with.
So any solution isn't really a path of "flip A then F then J" - it's "flip <these coins>; don't flip <these coins>". (It's unfortunate that the word "flip" is used for both the primary coin to flip and the secondary coins which change state for a particular move, but never mind - hopefully it's clear what I mean.)
Each coin will either be used as a primary move or not, 0 or 1. There are 16 coins, so 2^16 possibilities. So 0 might represent "don't do anything"; 1 might represent "just A"; 2 might represent "just B"; 3 "A and B" etc.
Test each combination. If (somehow) there's more than one solution, count the number of bits in each solution to find the least number.
Implementation hint: the "current state" can be represented as a 16 bit number as well. Using a particular coin as a primary move will always XOR the current state with a fixed number (for that coin). This makes it really easy to work out the effect of any particular combination of moves.
Okay, here's the solution in C#. It shows how many moves were required for each solution it finds, but it doesn't keep track of which moves those were, or what the least number of moves is. That's a SMOP :)
The input is a list of which coins are showing tails to start with - so for the example in the question, you'd start the program with an argument of "BEFGJLOP". Code:
using System;
public class CoinFlip
{
// All ints could really be ushorts, but ints are easier
// to work with
static readonly int[] MoveTransitions = CalculateMoveTransitions();
static int[] CalculateMoveTransitions()
{
int[] ret = new int[16];
for (int i=0; i < 16; i++)
{
int row = i / 4;
int col = i % 4;
ret[i] = PositionToBit(row, col) +
PositionToBit(row-1, col) +
PositionToBit(row+1, col) +
PositionToBit(row, col-1) +
PositionToBit(row, col+1);
}
return ret;
}
static int PositionToBit(int row, int col)
{
if (row < 0 || row > 3 || col < 0 || col > 3)
{
// Makes edge detection easier
return 0;
}
return 1 << (row * 4 + col);
}
static void Main(string[] args)
{
int initial = 0;
foreach (char c in args[0])
{
initial += 1 << (c-'A');
}
Console.WriteLine("Initial = {0}", initial);
ChangeState(initial, 0, 0);
}
static void ChangeState(int current, int nextCoin, int currentFlips)
{
// Reached the end. Success?
if (nextCoin == 16)
{
if (current == 0)
{
// More work required if we want to display the solution :)
Console.WriteLine("Found solution with {0} flips", currentFlips);
}
}
else
{
// Don't flip this coin
ChangeState(current, nextCoin+1, currentFlips);
// Or do...
ChangeState(current ^ MoveTransitions[nextCoin], nextCoin+1, currentFlips+1);
}
}
}
I would suggest a breadth first search, as someone else already mentioned.
The big secret here is to have multiple copies of the game board. Don't think of "the board."
I suggest creating a data structure that contains a representation of a board, and an ordered list of moves that got to that board from the starting position. A move is the coordinates of the center coin in a set of flips. I'll call an instance of this data structure a "state" below.
My basic algorithm would look something like this:
Create a queue.
Create a state that contains the start position and an empty list of moves.
Put this state into the queue.
Loop forever:
Pull first state off of queue.
For each coin showing tails on the board:
Create a new state by flipping that coin and the appropriate others around it.
Add the coordinates of that coin to the list of moves in the new state.
If the new state shows all heads:
Rejoice, you are done.
Push the new state into the end of the queue.
If you like, you could add a limit to the length of the queue or the length of move lists, to pick a place to give up. You could also keep track of boards that you have already seen in order to detect loops. If the queue empties and you haven't found any solutions, then none exist.
Also, a few of the comments already made seem to ignore the fact that the problem only allows coins that show tails to be in the middle of a move. This means that order very much does matter. If the first move flips a coin from heads to tails, then that coin can be the center of the second move, but it could not have been the center of the first move. Similarly, if the first move flips a coin from tails to heads, then that coin cannot be the center of the second move, even though it could have been the center of the first move.
The grid, read in row-major order, is nothing more than a 16 bit integer. Both the grid given by the problem and the 16 possible moves (or "generators") can be stored as 16 bit integers, thus the problems amounts to find the least possible number of generators which, summed by means of bitwise XOR, gives the grid itself as the result. I wonder if there's a smarter alternative than trying all the 65536 possibilities.
EDIT: Indeed there is a convenient way to do bruteforcing. You can try all the 1-move patterns, then all the 2-moves patterns, and so on. When a n-moves pattern matches the grid, you can stop, exhibit the winning pattern and say that the solution requires at least n moves. Enumeration of all the n-moves patterns is a recursive problem.
EDIT2: You can bruteforce with something along the lines of the following (probably buggy) recursive pseudocode:
// Tries all the n bit patterns with k bits set to 1
tryAllPatterns(unsigned short n, unsigned short k, unsigned short commonAddend=0)
{
if(n == 0)
tryPattern(commonAddend);
else
{
// All the patterns that have the n-th bit set to 1 and k-1 bits
// set to 1 in the remaining
tryAllPatterns(n-1, k-1, (2^(n-1) xor commonAddend) );
// All the patterns that have the n-th bit set to 0 and k bits
// set to 1 in the remaining
tryAllPatterns(n-1, k, commonAddend );
}
}
To elaborate on Federico's suggestion, the problem is about finding a set of the 16 generators that xor'ed together gives the starting position.
But if we consider each generator as a vector of integers modulo 2, this becomes finding a linear combination of vectors, that equal the starting position.
Solving this should just be a matter of gaussian elimination (mod 2).
EDIT:
After thinking a bit more, I think this would work:
Build a binary matrix G of all the generators, and let s be the starting state. We are looking for vectors x satisfying Gx=s (mod 2). After doing gaussian elimination, we either end up with such a vector x or we find that there are no solutions.
The problem is then to find the vector y such that Gy = 0 and x^y has as few bits set as possible, and I think the easiest way to find this would be to try all such y. Since they only depend on G, they can be precomputed.
I admit that a brute-force search would be a lot easier to implement, though. =)
Okay, here's an answer now that I've read the rules properly :)
It's a breadth-first search using a queue of states and the moves taken to get there. It doesn't make any attempt to prevent cycles, but you have to specify a maximum number of iterations to try, so it can't go on forever.
This implementation creates a lot of strings - an immutable linked list of moves would be neater on this front, but I don't have time for that right now.
using System;
using System.Collections.Generic;
public class CoinFlip
{
struct Position
{
readonly string moves;
readonly int state;
public Position(string moves, int state)
{
this.moves = moves;
this.state = state;
}
public string Moves { get { return moves; } }
public int State { get { return state; } }
public IEnumerable<Position> GetNextPositions()
{
for (int move = 0; move < 16; move++)
{
if ((state & (1 << move)) == 0)
{
continue; // Not allowed - it's already heads
}
int newState = state ^ MoveTransitions[move];
yield return new Position(moves + (char)(move+'A'), newState);
}
}
}
// All ints could really be ushorts, but ints are easier
// to work with
static readonly int[] MoveTransitions = CalculateMoveTransitions();
static int[] CalculateMoveTransitions()
{
int[] ret = new int[16];
for (int i=0; i < 16; i++)
{
int row = i / 4;
int col = i % 4;
ret[i] = PositionToBit(row, col) +
PositionToBit(row-1, col) +
PositionToBit(row+1, col) +
PositionToBit(row, col-1) +
PositionToBit(row, col+1);
}
return ret;
}
static int PositionToBit(int row, int col)
{
if (row < 0 || row > 3 || col < 0 || col > 3)
{
return 0;
}
return 1 << (row * 4 + col);
}
static void Main(string[] args)
{
int initial = 0;
foreach (char c in args[0])
{
initial += 1 << (c-'A');
}
int maxDepth = int.Parse(args[1]);
Queue<Position> queue = new Queue<Position>();
queue.Enqueue(new Position("", initial));
while (queue.Count != 0)
{
Position current = queue.Dequeue();
if (current.State == 0)
{
Console.WriteLine("Found solution in {0} moves: {1}",
current.Moves.Length, current.Moves);
return;
}
if (current.Moves.Length == maxDepth)
{
continue;
}
// Shame Queue<T> doesn't have EnqueueRange :(
foreach (Position nextPosition in current.GetNextPositions())
{
queue.Enqueue(nextPosition);
}
}
Console.WriteLine("No solutions");
}
}
If you are practicing for the ACM, I would consider this puzzle also for non-trivial boards, say 1000x1000. Brute force / greedy may still work, but be careful to avoid exponential blow-up.
The is the classic "Lights Out" problem. There is actually an easy O(2^N) brute force solution, where N is either the width or the height, whichever is smaller.
Let's assume the following works on the width, since you can transpose it.
One observation is that you don't need to press the same button twice - it just cancels out.
The key concept is just that you only need to determine if you want to press the button for each item on the first row. Every other button press is uniquely determined by one thing - whether the light above the considered button is on. If you're looking at cell (x,y), and cell (x,y-1) is on, there's only one way to turn it off, by pressing (x,y). Iterate through the rows from top to bottom and if there are no lights left on at the end, you have a solution there. You can then take the min of all the tries.
It's a finite state machine, where each "state" is the 16 bit integer corresponding the the value of each coin.
Each state has 16 outbound transitions, corresponding to the state after you flip each coin.
Once you've mapped out all the states and transitions, you have to find the shortest path in the graph from your beginning state to state 1111 1111 1111 1111,
I sat down and attempted my own solution to this problem (based on the help I received in this thread). I'm using a 2d array of booleans, so it isn't as nice as the people using 16bit integers with bit manipulation.
In any case, here is my solution in Java:
import java.util.*;
class Node
{
public boolean[][] Value;
public Node Parent;
public Node (boolean[][] value, Node parent)
{
this.Value = value;
this.Parent = parent;
}
}
public class CoinFlip
{
public static void main(String[] args)
{
boolean[][] startState = {{true, false, true, true},
{false, false, false, true},
{true, false, true, false},
{true, true, false, false}};
List<boolean[][]> solutionPath = search(startState);
System.out.println("Solution Depth: " + solutionPath.size());
for(int i = 0; i < solutionPath.size(); i++)
{
System.out.println("Transition " + (i+1) + ":");
print2DArray(solutionPath.get(i));
}
}
public static List<boolean[][]> search(boolean[][] startState)
{
Queue<Node> Open = new LinkedList<Node>();
Queue<Node> Closed = new LinkedList<Node>();
Node StartNode = new Node(startState, null);
Open.add(StartNode);
while(!Open.isEmpty())
{
Node nextState = Open.remove();
System.out.println("Considering: ");
print2DArray(nextState.Value);
if (isComplete(nextState.Value))
{
System.out.println("Solution Found!");
return constructPath(nextState);
}
else
{
List<Node> children = generateChildren(nextState);
Closed.add(nextState);
for(Node child : children)
{
if (!Open.contains(child))
Open.add(child);
}
}
}
return new ArrayList<boolean[][]>();
}
public static List<boolean[][]> constructPath(Node node)
{
List<boolean[][]> solutionPath = new ArrayList<boolean[][]>();
while(node.Parent != null)
{
solutionPath.add(node.Value);
node = node.Parent;
}
Collections.reverse(solutionPath);
return solutionPath;
}
public static List<Node> generateChildren(Node parent)
{
System.out.println("Generating Children...");
List<Node> children = new ArrayList<Node>();
boolean[][] coinState = parent.Value;
for(int i = 0; i < coinState.length; i++)
{
for(int j = 0; j < coinState[i].length; j++)
{
if (!coinState[i][j])
{
boolean[][] child = arrayDeepCopy(coinState);
flip(child, i, j);
children.add(new Node(child, parent));
}
}
}
return children;
}
public static boolean[][] arrayDeepCopy(boolean[][] original)
{
boolean[][] r = new boolean[original.length][original[0].length];
for(int i=0; i < original.length; i++)
for (int j=0; j < original[0].length; j++)
r[i][j] = original[i][j];
return r;
}
public static void flip(boolean[][] grid, int i, int j)
{
//System.out.println("Flip("+i+","+j+")");
// if (i,j) is on the grid, and it is tails
if ((i >= 0 && i < grid.length) && (j >= 0 && j <= grid[i].length))
{
// flip (i,j)
grid[i][j] = !grid[i][j];
// flip 1 to the right
if (i+1 >= 0 && i+1 < grid.length) grid[i+1][j] = !grid[i+1][j];
// flip 1 down
if (j+1 >= 0 && j+1 < grid[i].length) grid[i][j+1] = !grid[i][j+1];
// flip 1 to the left
if (i-1 >= 0 && i-1 < grid.length) grid[i-1][j] = !grid[i-1][j];
// flip 1 up
if (j-1 >= 0 && j-1 < grid[i].length) grid[i][j-1] = !grid[i][j-1];
}
}
public static boolean isComplete(boolean[][] coins)
{
boolean complete = true;
for(int i = 0; i < coins.length; i++)
{
for(int j = 0; j < coins[i].length; j++)
{
if (coins[i][j] == false) complete = false;
}
}
return complete;
}
public static void print2DArray(boolean[][] array)
{
for (int row=0; row < array.length; row++)
{
for (int col=0; col < array[row].length; col++)
{
System.out.print((array[row][col] ? "H" : "T") + " ");
}
System.out.println();
}
}
}

Resources