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.


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)


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.


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 .

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.