import java.io.*; import java.math.BigInteger; /* * A (slightly more intelligent) brute force solver for the Hat Puzzle * Alan Davidson, May 2007 * * This code is released under the Creative Commons Attribution-Share Alike 3.0 * License. You are welcome to use this code for any purpose you want, with the * following two conditions: * * You must cite me if you reference this work * * If you modify or build upon this program, you can only distribute the * resulting work under the Creative Commons Attribution-Share Alike * License or a compatible license. * * For more information about this license, see * http://creativecommons.org/licenses/by-sa/3.0/ * * * The Hat Puzzle is as follows: N people enter a room and have a hat placed on * each of their heads. Each hat is either red or blue (and the chances that a * given hat is read are 50%). Everyone can see everyone else's hat color, but * not their own. No one is allowed to comumnicate with each other during this * time. Each person is then lead into a separate room, and asked what color * his or her hat is. However, any person may refuse to answer this question. * The group wins if at least one person answers correctly and no one answers * incorrectly. The object of the group is to find a strategy that maximizes * their chances of winning. * * This program finds the brute force solution to the problem, for a given N, * using error-correcting codes: every time someone guesses wrong, they are 1 * bit-flip away from guessing right. If we can get everyone to guess wrong * together, we can guarantee N right guesses 1 bit-flip away. So now, we try * to maximize the number of right guesses we have by searching through * combinations of numbers on which to guess wrong. * * * The input of the program should be lines of the form N,dominators (for * instance, 5,7 will find the best solution when N=5 and you can have at most * 7 states in which players guess wrong. * * The output will be a string of bits, each one corresponding to a hat * configuration. The rightmost bit corresponds to configuration 0, the next * one corresponds to configuration 1, then configuration 2, etc. If the bit is * a 1, that configuration is in the list being described (winning * configurations or losing configurations). If the bit is a 0, it is not in * that list. Note that there are probably a bunch of other 0's to the left of * the bits printed out; I'm not going to bother fixing that. * * The program also prints out how far it is through searching the solution * space. This lets you get an estimate of how long it will take to finish * running. Moreover, it prints a description every time it finds a better * strategy than the ones it has seen before. This lets you look at the best * strategy the program found, even if you halt the program before it has * searched everywhere. */ class hat{ static void p(Object o){System.out.print(o);} static void pl(Object o){System.out.print("" + o + "\n");} static void p(int o){System.out.print(o);} static void pl(int o){System.out.println(o);} static int N; // the number of people in the group static int dominators; // The number of configurations we can get wrong static int configs; // Number of possible hat configurations: 2^N static BigInteger[] all_masks; // Numbers that win when the index is wrong // The best_*_positions are masks containing the numbers where the group // either wins or loses from guessing (rather than losing due to everyone // passing). These masks store the best configuration we've found so far. static BigInteger best_fail_positions; static BigInteger best_win_positions; public static void main(String[] args){ BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); try{ String input_line; while((input_line = in.readLine()) != null){ String[] inp = input_line.split(","); N = Integer.parseInt(inp[0]); dominators = Integer.parseInt(inp[1]); configs = 1 << N; // 2 possibilities for each hat pl("Trying to solve the puzzle for " + N + " people and " + dominators + " losing configurations"); solve(); } }catch(Exception e){ e.printStackTrace();} } public static void solve() { // Initially, everyone should pass all the time. We'll add in other // behavior as we search. best_fail_positions = BigInteger.ZERO; best_win_positions = BigInteger.ZERO; initAllMasks(); enumerateAndAnalyze(0, BigInteger.ZERO, BigInteger.ZERO); printSolution(); } public static void enumerateAndAnalyze(int current_dominators, BigInteger winners, BigInteger losers) { int num_correct = winners.bitCount(); int best_num_correct = best_win_positions.bitCount(); if (num_correct > best_num_correct) { pl("new best strategy! it is successful " + num_correct + " times."); best_num_correct = num_correct; best_fail_positions = losers; best_win_positions = winners; pl("winners: " + best_win_positions.toString(2)); pl("losers: " + best_fail_positions.toString(2)); } if(current_dominators != dominators) { for(int i = 0; i < configs; ++i) { if (current_dominators == 0) { pl("Now on number " + i + " of " + configs + "..."); } BigInteger new_winners = winners.or(all_masks[i]); BigInteger updated_losers = losers.setBit(i); BigInteger updated_winners = new_winners.and( new_winners.xor(updated_losers)); enumerateAndAnalyze(current_dominators + 1, updated_winners, updated_losers); } } } public static void printSolution() { int best_num_correct = best_win_positions.bitCount(); pl("The best strategy we came up with had " + best_num_correct + " correct answers"); pl("Winning bits: " + best_win_positions.toString(2)); pl("Losing bits: " + best_fail_positions.toString(2)); } public static void initAllMasks() { all_masks = new BigInteger[configs]; for (int i = 0; i < configs; ++i) { all_masks[i] = BigInteger.ZERO; BigInteger current = BigInteger.valueOf(i); for (int j = 0; j < N; ++j) { current = current.flipBit(j); all_masks[i] = all_masks[i].flipBit(current.intValue()); current = current.flipBit(j); } } } }