Estratégia ideal para um jogo abstrato

12

Eu recebi o seguinte problema em uma entrevista (que eu já não consegui resolver, sem tentar me enganar): O jogo começa com um número inteiro positivo . (Por exemplo, A 0 = 1234. ) Esse número é convertido em representação binária e N é o número de bits definido como 1 . (Por exemplo, A 0 = b 100 1101 0010 , N = 5. )A0A0=1234N1A0=b100 1101 0010N=5.

O jogador 1 escolhe um número menor que A 0 . B 0 deve ter apenas um bit definido como 1. (Por exemplo, B 0 = b 10 0000 0000 = 512. ) Seja A 1 = A 0 - B 0 . (Por exemplo, A 1 = 1234 - 512 = 722 = b 10 1101 0010. ) Um movimento é válido se B 0B0A0B0B0=b10 0000 0000=512A1=A0B0A1=1234512=722=b1011010010B0satisfaz as restrições anteriores e se o número de bits definido em A1 é ainda igual a N .

O jogador 2 continua de escolhendo um B 1 válido e o jogador 1 continua de A 2A1B1A2 , e assim por diante. Um jogador perde se não tiver mais movimentos válidos.

Supondo que os dois jogadores joguem da melhor maneira, determine o jogador vencedor usando um método razoavelmente eficiente. (Na minha definição de problema, as restrições eram que o programa precisava fornecer uma solução para alguns milhões de números de entrada que se encaixam em um número inteiro de 32 bits assinado.) Ou seja, a solução não precisa ser totalmente analítico.


Meu interesse pessoal aqui é descobrir se a expectativa de mim de ter encontrado e implementado a solução correta sem nenhum retorno sobre a correção nos 120 minutos que me foram dados era razoável; ou se essa foi uma das perguntas "vamos ver se eles já viram esse quebra-cabeça antes".

Eu falhei porque escolhi implementar o que parecia ser uma estratégia razoável, que me deu resultados corretos para os poucos casos de teste que recebi de frente, perdi muito tempo agilizando a execução e acabei entregando incorretamente saída total como o meu tempo acabou.

Em retrospecto, eu deveria ter implementado uma busca por força bruta e memorizado soluções parciais para pequenos números iniciais, mas a retrospectiva é sempre 20/20. No entanto, estou curioso para saber se existe uma abordagem comum diferente que me iludiu como desonesta.

millimoose
fonte
Pela descrição, não entendi que os movimentos escolhidos devem ter um único bit definido como 1 (pensei que era apenas parte do exemplo).
Jjmontes
@jjmontes - É declarada como a primeira regra para a escolha de um número B - todos os exemplos são indicados como tal, tudo fora dos parênteses é geral. Você tem alguma sugestão de como isso poderia ter sido mais claro?
Millimoose
1
B0A0
@Veedrac - Cara, se eu soubesse disso, todo meu esforço para fazer a contagem de bits correr razoavelmente rápido não teria sido um desperdício. Uma resposta explicando por que funciona seria excelente.
Millimoose
@millimoose Bitcount está no hardware da maioria das CPUs modernas!
Veedrac

Respostas:

21

011001

1001

Portanto, o único fator determinante em um jogo é quantos swaps são necessários para chegar ao estado em que todos estão à direita e não há estratégia de ganhar ou perder. A paridade do número de swaps necessários é o único fator determinante.

1 s não pode atravessar um ao outro, por isso, se nós numerados los e monitorados-los através de swaps que eles permanecem na mesma ordem no estado final. Cada troca os aproxima um da sua posição final.

i1101kki

i = 0
k = 0
total = 0
while n > 0:
    if n & 1:
       total += k - i
       i += 1
    n >>= 1
    k += 1

totalO(logn)

orlp
fonte
Parece certo, eu me deparei com alguns trechos dessa abordagem após a entrega. Acho que eu terminei ao pular a arma ao começar a codificar e ficar atolado na resultante caça aos ganso selvagem.
Millimoose
Pensando nisso, o fato da estratégia não importa meios I quer ter um bug bastante obscura em minha implementação, ou ele deveria ter produzido os mesmos resultados que qualquer outra aplicação que joga o jogo corretamente ...
millimoose
5

Uma maneira de resolver esse problema é a seguinte:

  • Encontre a solução para alguns valores simples usando a abordagem "força bruta memorizada" que você está sugerindo.

  • Adivinhe a resposta (quais posições estão ganhando e quais estão perdendo).

  • Tente provar sua resposta. Se você conseguir, ótimo. Caso contrário, tente encontrar um contra-exemplo e use-o para adivinhar outra resposta. Aqui, pode ser útil resolver mais alguns casos.

É realmente difícil dizer quanto tempo leva. No entanto, em entrevistas, não se espera necessariamente que você encontre a solução. Em vez disso, os entrevistadores querem saber como você abordou a solução do problema e que progresso você conseguiu fazer.

Yuval Filmus
fonte
Sim, não, eles me rejeitaram porque minha saída estava errada e eu fiquei sem tempo.
millimoose
A abordagem da força bruta memorizada estaria correta, uma vez que não são necessários atalhos em relação à estratégia. No entanto, eu também - imaginei - seria incomparavelmente lento, e a memorização pode ter sido muito trabalhosa demais para possivelmente não ter muita ajuda sem o uso de pequenas quantidades de memória. Ou talvez não, tentarei isso mais tarde apenas para limpar isso do meu sistema.
millimoose
5

Observe na resposta do @ orlp que queremos a paridade da soma dos deslocamentos da posição inicial para a posição final. Vamos anotar isso:

       9876543210
       9 76 4  1    (positions at start)
start: 1011010010
end:   0000011111
            43210   (positions at end)

Então nós queremos

  ((1 - 0) + (4 - 1) + (6 - 2) + (7 - 3) + (9 - 4)) & 1
= ((1 + 4 + 6 + 7 + 9) - (0 + 1 + 2 + 3 + 4)) & 1
= ((1 + 0 + 0 + 1 + 1) - (0 + 1 + 0 + 1 + 0)) & 1

A primeira parte é apenas a paridade do número de bits nas posições ímpares. Você pode mascarar isso usando o número máximo não assinado máximo, dividindo por 0b11 e negando.

= (bitcount(x & ~(UINT_MAX / 0b11)) ^ (0 + 1 + 0 + 1 + 0)) & 1

A segunda parte é a paridade de metade do número de bits x.

= (bitcount(x & ~(UINT_MAX / 0b11)) ^ (bitcount(x) >> 1)) & 1

bitcountpode usar a popcntinstrução de hardware ou pode ser implementado manualmente, utilizando apenas o último ou o penúltimo bit, com reduções rápidas como essa .

Veedrac
fonte