Eu tenho uma n x m
matriz que consiste em números inteiros não negativos. Por exemplo:
2 3 4 7 1
1 5 2 6 2
4 3 4 2 1
2 1 2 4 1
3 1 3 4 1
2 1 4 3 2
6 9 1 6 4
"Soltar uma bomba" diminui em um o número da célula-alvo e todas as oito vizinhas, para um mínimo de zero.
x x x
x X x
x x x
O que é um algoritmo que determinaria o número mínimo de bombas necessárias para reduzir todas as células a zero?
Opção B (por eu não ser um leitor cuidadoso)
Na verdade, a primeira versão do problema não é a que estou procurando resposta. Não li cuidadosamente toda a tarefa, há restrições adicionais, digamos:
E o problema simples, quando a sequência na linha não deve aumentar:
8 7 6 6 5
é possível sequência de entrada
7 8 5 5 2
não é possível porque 7 -> 8 cresce em uma sequência.
Talvez encontrar resposta para o caso "mais fácil" ajudaria a encontrar a solução para um caso mais difícil.
PS: Acredito que, quando temos várias situações iguais, exigem bombas mínimas para limpar a linha superior, escolhemos uma que use a maioria das bombas no "lado esquerdo" da linha. Ainda há alguma prova que possa estar correta?
fonte
what's the minimum amount of bombs required to clean the board?
isso significa que não é necessariamente necessário encontrar um padrão de bombardeio real, mas apenas o número mínimo de bombas?Respostas:
Existe uma maneira de reduzir isso para um simples sub-problema.
Existem duas partes na explicação, o algoritmo e o motivo pelo qual o algoritmo fornece uma solução ideal. O primeiro não fará sentido sem o segundo, então vou começar com o porquê.
Se você pensa em bombardear o retângulo (assuma um retângulo grande - ainda não há arestas), poderá ver que a única maneira de reduzir o retângulo oco de quadrados no perímetro para 0 é bombardear o perímetro ou bombardear o retângulo oco de quadrados dentro do perímetro. Vou chamar a camada de perímetro 1 e o retângulo dentro dela de camada 2.
Um insight importante é que não há ponto de bombardeio na camada 1, porque o "raio de explosão" obtido é sempre contido no raio de explosão de outro quadrado da camada 2. Você deve conseguir se convencer disso facilmente.
Portanto, podemos reduzir o problema para encontrar uma maneira ideal de bombardear o perímetro e repetir isso até que todos os quadrados sejam 0.
Mas é claro que isso nem sempre encontrará uma solução ideal se for possível bombear o perímetro de uma maneira menos do que ótima, mas usando X bombas extras, o problema de reduzir a camada interna é mais simples por> X bombas. Portanto, se chamarmos a camada permissora de um, se colocarmos bombas X extras em algum lugar da camada 2 (apenas dentro da camada 1), podemos reduzir o esforço de bombardear a camada 2 mais tarde em mais de X? Em outras palavras, temos que provar que podemos ser gananciosos na redução do perímetro externo.
Mas sabemos que podemos ser gananciosos. Como nenhuma bomba na camada 2 pode ser mais eficiente na redução da camada 2 para 0 do que uma bomba estrategicamente colocada na camada 3. E pelo mesmo motivo de antes - sempre há uma bomba que podemos colocar na camada 3 que afetará todos os quadrados da camada 2 que uma bomba colocada na camada 2 pode. Portanto, nunca pode nos prejudicar sermos gananciosos (nesse sentido de gananciosos).
Então, tudo o que precisamos fazer é encontrar a maneira ideal de reduzir o permiter para 0 bombardeando a próxima camada interna.
Nunca nos machucamos ao bombardear primeiro o canto para 0, porque apenas o canto da camada interna pode alcançá-lo, então realmente não temos escolha (e, qualquer bomba no perímetro que pode alcançar o canto tem um raio de explosão contido no raio de explosão do canto da camada interna).
Uma vez feito isso, os quadrados no perímetro adjacente ao canto 0 podem ser alcançados apenas por 2 quadrados da camada interna:
Nesse ponto, o perímetro é efetivamente um loop unidimensional fechado, porque qualquer bomba reduzirá 3 quadrados adjacentes. Exceto por alguma estranheza perto dos cantos - X pode "acertar" A, B, C e D.
Agora, não podemos usar nenhum truque de raio de explosão - a situação de cada quadrado é simétrica, exceto nos cantos estranhos, e mesmo não há raio de explosão é um subconjunto de outro. Observe que se essa fosse uma linha (como o coronel Panic discute) em vez de um circuito fechado, a solução é trivial. Os pontos finais devem ser reduzidos a 0 e nunca é prejudicial bombardear os pontos adjacentes aos pontos finais, novamente porque o raio de explosão é um superconjunto. Depois de ter feito o seu ponto final 0, você ainda terá um novo ponto final; portanto, repita (até que a linha esteja com todos os 0).
Portanto, se podemos reduzir de maneira ideal um único quadrado da camada para 0, temos um algoritmo (porque cortamos o loop e agora temos uma linha reta com pontos finais). Acredito que bombardeios adjacentes ao quadrado com o valor mais baixo (dando-lhe 2 opções), de modo que o valor mais alto dentro de 2 quadrados desse valor mais baixo seja o mínimo possível (talvez você precise dividir seu bombardeio para gerenciar isso) será o ideal, mas eu ainda não tem uma prova.
fonte
But, we do know we can be greedy...
- Eu não estou comprando isso. Considere1 1 2 1 1 2
no perímetro. O número mínimo de bombas é 4, mas existem três soluções distintas. Cada solução tem um impacto diferente na próxima camada. Desde que haja várias soluções mínimas para um perímetro, você não poderá isolar completamente o perímetro sem considerar as camadas internas. Realmente não acho que esse problema possa ser resolvido sem retroceder.0011100
0100010
0000000
0000000
1110111
. A maneira ideal de bombardear a primeira camada é bombardear no meio da segunda linha, levando um total de três bombas para matar a camada externa. Mas então você precisa de duas bombas para cuidar da próxima camada. O ideal requer apenas quatro bombas no total: duas nas duas primeiras linhas e duas na última linha.Pólya diz: "Se você não pode resolver um problema, existe um problema mais fácil que você pode resolver: encontre-o".
O problema óbvio mais simples é o problema unidimensional (quando a grade é uma única linha). Vamos começar com o algoritmo mais simples - bombardear avidamente o maior alvo. Quando isso dá errado?
Dado
1 1 1
, o algoritmo ganancioso é indiferente a qual célula ele bombeia primeiro. Obviamente, a célula central é melhor - zera todas as três células ao mesmo tempo. Isso sugere um novo algoritmo A, "bomba para minimizar a soma restante". Quando esse algoritmo dá errado?Dado
1 1 2 1 1
, o algoritmo A é indiferente entre o bombardeio da 2ª, 3ª ou 4ª células. Mas bombardear a 2ª célula para sair0 0 1 1 1
é melhor do que bombardear a 3ª célula para sair1 0 1 0 1
. Como consertar isso? O problema com o bombardeio da 3ª célula é que ela nos deixa trabalhando para a esquerda e para a direita, o que deve ser feito separadamente.Que tal "bomba para minimizar a soma restante, mas maximizar o mínimo para a esquerda (de onde bombardeamos) mais o mínimo para a direita". Chame esse algoritmo B. Quando esse algoritmo dá errado?
Edit: Depois de ler os comentários, concordo que um problema muito mais interessante seria o problema unidimensional alterado para que as extremidades se unam. Adoraria ver algum progresso nisso.
fonte
Eu tive que parar com apenas uma solução parcial, pois estava sem tempo, mas espero que até essa solução parcial forneça algumas idéias sobre uma possível abordagem para solucionar esse problema.
Quando enfrento um problema difícil, gosto de apresentar problemas mais simples para desenvolver uma intuição sobre o espaço do problema. Aqui, o primeiro passo que tomei foi reduzir esse problema 2-D em um problema 1-D. Considere uma linha:
De uma forma ou de outra, você sabe que precisará bombardear o
4
local 4 vezes ou ao redor dele para reduzi-lo a 0. Como o lado esquerdo do local é um número menor, não há benefício em bombardear o0
ou4
bombardear o excesso2
. Na verdade, eu acredito (mas falta uma prova rigorosa) de que o bombardeio2
até o4
ponto chegar a 0 é pelo menos tão bom quanto qualquer outra estratégia para reduzi-4
lo a 0. Pode-se prosseguir na linha da esquerda para a direita em uma estratégia como isso:Alguns exemplos de ordens de bombardeio:
A idéia de começar com um número que precisa diminuir de uma forma ou de outra é atraente, porque de repente se torna possível encontrar uma solução que, como alguns afirmam ser pelo menos tão boa quanto todas as outras soluções.
O próximo passo na complexidade, onde essa busca pelo menos boa ainda é viável, está no limite do quadro. É claro para mim que nunca há nenhum benefício estrito para bombardear a borda externa; é melhor bombardear o local um e obter três outros espaços de graça. Diante disso, podemos dizer que bombardear o anel um dentro da borda é pelo menos tão bom quanto bombardear a borda. Além disso, podemos combinar isso com a intuição de que bombardear o caminho certo dentro da borda é realmente a única maneira de reduzir os espaços da borda para zero. Ainda mais, é trivialmente simples descobrir a estratégia ideal (na medida em que menos bom do que qualquer outra estratégia) para reduzir os números de canto para zero. Reunimos tudo isso e podemos nos aproximar muito mais de uma solução no espaço 2D.
Dada a observação sobre as peças dos cantos, podemos dizer com certeza que conhecemos a estratégia ideal para ir de qualquer quadro inicial a um quadro com zeros em todos os cantos. Este é um exemplo desse quadro (peguei emprestado os números dos dois quadros lineares acima). Eu rotulei alguns espaços de maneira diferente e vou explicar o porquê.
Um vai notar na linha superior e realmente se assemelha ao exemplo linear vimos anteriormente. Lembrando nossa observação anterior de que a maneira ideal de obter a linha superior até 0 é bombardear a segunda linha (a
x
linha). Não há como limpar a linha superior bombardeando qualquer uma dasy
linhas e nenhum benefício adicional para bombardear a linha superior bombardeando o espaço correspondente nax
linha.Nós poderia aplicar a estratégia linear de cima (a bombardear os espaços correspondentes na
x
linha), preocupando-nos unicamente com a linha superior e nada mais. Seria algo como isto:A falha nessa abordagem se torna muito óbvia nos dois atentados finais. É claro, dado que os únicos sites de bombas que reduzem o
4
número na primeira coluna da segunda linha são o primeirox
e oy
. Os dois bombardeios finais são claramente inferiores ao bombardeio do primeirox
, o que teria feito exatamente o mesmo (com relação ao primeiro ponto na linha superior, que não temos outra maneira de limpar). Como demonstramos que nossa estratégia atual é abaixo do ideal, é claramente necessária uma modificação na estratégia.Neste ponto, posso dar um passo atrás na complexidade e focar apenas um canto. Vamos considerar este:
É claro a única maneira de obter os espaços com
4
baixo a zero estão a bombardear alguma combinação dex
,y
ez
. Com algumas acrobacias em mente, tenho quase certeza de que a solução ideal é bombardearx
três vezes ea
depoisb
. Agora é uma questão de descobrir como cheguei a essa solução e se ela revela alguma intuição que podemos usar para resolver esse problema local. Percebo que não há bombardeiosy
ez
espaços. Tentar encontrar um canto onde bombardear esses espaços faz sentido produz um canto mais ou menos assim:Para este, está claro para mim que a solução ideal é bombardear
y
5 vezes ez
5 vezes. Vamos dar um passo adiante.Aqui, ele se sente da mesma forma intuitiva de que a melhor solução é bombardear
a
eb
6 vezes e depoisx
4 vezes.Agora, torna-se um jogo de como transformar essas intuições em princípios nos quais podemos construir.
Espero que continue!
fonte
Para perguntas atualizadas, um algoritmo simples e ganancioso fornece o melhor resultado.
Solte as bombas A [0,0] na célula A [1,1], depois solte as bombas A [1,0] na célula A [2,1] e continue esse processo para baixo. Para limpar o canto inferior esquerdo, solte o máximo de bombas (A [N-1,0], A [N-2,0], A [N-3,0]) na célula A [N-2,1]. Isso limpará completamente as três primeiras colunas.
Com a mesma abordagem, limpe as colunas 3,4,5, depois as colunas 6,7,8, etc.
Infelizmente, isso não ajuda a encontrar a solução para o problema original.
Problema "maior" (sem restrição "não-decrescente") pode ser comprovadamente difícil para NP. Aqui está o esboço de uma prova.
Suponha que tenhamos um gráfico plano de grau até 3. Vamos encontrar uma cobertura mínima de vértices para este gráfico. Segundo o artigo da Wikipedia, esse problema é NP-difícil para gráficos planares de até 3. Isso pode ser comprovado pela redução do Planar 3SAT. E dureza do Planar 3SAT - pela redução do 3SAT. Ambas as provas são apresentadas em palestras recentes em "Limites inferiores algorítmicos" pelo prof. Erik Demaine (palestras 7 e 9).
Se dividirmos algumas arestas do gráfico original (gráfico à esquerda no diagrama), cada uma com um número par de nós adicionais, o gráfico resultante (gráfico à direita no diagrama) deve ter exatamente a mesma cobertura mínima de vértices para os vértices originais. Essa transformação permite alinhar vértices do gráfico a posições arbitrárias na grade.
Se colocarmos vértices gráficos apenas em linhas e colunas pares (de maneira que nenhuma aresta incida em um vértice forme um ângulo agudo), insira "ones" onde houver uma aresta e insira "zeros" em outras posições da grade, poderíamos usar qualquer solução para o problema original para encontrar uma cobertura mínima de vértices.
fonte
Você pode representar esse problema como um problema de programação inteira . (esta é apenas uma das soluções possíveis para abordar esse problema)
Tendo pontos:
pode-se escrever 16 equações onde, por exemplo, o ponto f
minimizado sobre a soma de todos os índices e solução inteira.
A solução é obviamente a soma desses índices.
Isso pode ser simplificado ainda mais, definindo todos os xi nos limites 0, para que você tenha a equação 4 + 1 neste exemplo.
O problema é que não existe um algoritmo trivial para resolver esses problemas. Não sou especialista nisso, mas resolver esse problema porque a programação linear é difícil para o NP.
fonte
Esta é uma resposta parcial, estou tentando encontrar um limite inferior e um limite superior que possam ser o número possível de bombas.
Em placas 3x3 e menores, a solução é sempre trivialmente a maior célula numerada.
Em placas maiores que 4x4, o primeiro limite inferior óbvio é a soma dos cantos:
por mais que você organize a bomba, é impossível limpar esta placa 4x4 com menos de 2 + 1 + 6 + 4 = 13 bombas.
Já foi mencionado em outras respostas que colocar a bomba na segunda esquina para eliminar a esquina nunca é pior do que colocar a bomba na própria esquina, de acordo com o quadro:
Podemos zerar os cantos colocando bombas no segundo para o canto para dar um novo quadro:
Por enquanto, tudo bem. Precisamos de 13 bombas para limpar os cantos.
Agora observe o número 6, 4, 3 e 2 marcado abaixo:
Não há como bombardear duas dessas células usando uma única bomba; portanto, a bomba mínima aumentou 6 + 4 + 3 + 2; portanto, aumentando o número de bombas que usamos para limpar os cantos, obtemos o mínimo o número de bombas necessárias para este mapa se tornou 28 bombas. É impossível limpar este mapa com menos de 28 bombas, este é o limite inferior para este mapa.
Você pode usar o algoritmo ganancioso para estabelecer um limite superior. Outras respostas mostraram que um algoritmo ganancioso produz uma solução que usa 28 bombas. Como já provamos anteriormente que nenhuma solução ideal pode ter menos de 28 bombas, portanto 28 bombas são realmente uma solução ideal.
Quando ganancioso e o método para encontrar o limite mínimo que mencionei acima não convergem, acho que você precisa voltar a verificar todas as combinações.
O algoritmo para encontrar o limite inferior é o seguinte:
minimums
lista.minimums
lista para obter o limite inferior.fonte
Esta seria uma abordagem gananciosa:
Calcule uma matriz de "pontuação" da ordem n X m, em que pontuação [i] [j] é a dedução total de pontos na matriz se a posição (i, j) for bombardeada. (A pontuação máxima de um ponto é 9 e a pontuação mínima é 0)
Movendo-se em linha, encontre e escolha a primeira posição com a maior pontuação (digamos (i, j)).
Bomba (i, j). Aumente a contagem de bombas.
Se todos os elementos da matriz original não forem zero, vá para 1.
Tenho minhas dúvidas de que esta seja a solução ideal.
Editar:
A abordagem gananciosa que publiquei acima, enquanto funciona, provavelmente não nos dá a solução ideal. Então, achei que deveria adicionar alguns elementos do DP a ele.
Acho que podemos concordar que, a qualquer momento, uma das posições com a "pontuação" mais alta (pontuação [i] [j] = dedução total de pontos se (i, j) for bombardeado) deve ser direcionada. Começando com essa suposição, aqui está a nova abordagem:
NumOfBombs (M): (retorna o número mínimo de atentados necessários)
Dada uma matriz M de ordem n X m. Se todos os elementos de M forem zero, retorne 0.
Calcular a matriz "score" M.
Seja k posições distintas P1, P2, ... Pk (1 <= k <= n * m), sejam as posições em M com as pontuações mais altas.
return (1 + min (NumOfBombs (M1), NumOfBombs (M2), ..., NumOfBombs (Mk))))
onde M1, M2, ..., Mk são as matrizes resultantes se bombardearmos as posições P1, P2, ..., Pk, respectivamente.
Além disso, se quisermos que a ordem das posições seja adicional, além disso, teremos que acompanhar os resultados de "min".
fonte
Seu novo problema, com os valores não decrescentes nas linhas, é bastante fácil de resolver.
Observe que a coluna da esquerda contém os números mais altos. Portanto, qualquer solução ideal deve primeiro reduzir esta coluna a zero. Assim, podemos executar um bombardeio 1-D sobre esta coluna, reduzindo todos os elementos nela a zero. Deixamos as bombas caírem na segunda coluna para causar danos máximos. Existem muitos posts aqui lidando com o caso 1D, eu acho, então me sinto seguro em pular esse caso. (Se você quiser que eu descreva, eu posso). Por causa da propriedade decrescente, as três colunas mais à esquerda serão reduzidas a zero. Mas, provavelmente usaremos um número mínimo de bombas aqui porque a coluna da esquerda deve ser zerada.
Agora, depois que a coluna esquerda estiver zerada, apenas cortamos as três colunas mais à esquerda que agora estão zeradas e repetimos com a matriz agora reduzida. Isso deve nos dar uma solução ótima, pois em cada estágio usamos um número comprovadamente mínimo de bombas.
fonte
Programação linear inteira do Mathematica usando desvio e vinculação
Como já foi mencionado, esse problema pode ser resolvido usando a programação linear inteira (que é NP-Hard ). O Mathematica já possui ILP embutido.
"To solve an integer linear programming problem Mathematica first solves the equational constraints, reducing the problem to one containing inequality constraints only. Then it uses lattice reduction techniques to put the inequality system in a simpler form. Finally, it solves the simplified optimization problem using a branch-and-bound method."
[Consulte Tutorial de otimização restrita no Mathematica ..]Eu escrevi o seguinte código que utiliza bibliotecas ILP do Mathematica. É surpreendentemente rápido.
Para o exemplo fornecido no problema:
Saídas
Para quem lê isso com um algoritmo ganancioso
Experimente o seu código no seguinte problema 10x10:
Aqui está separado por vírgulas:
Para esse problema, minha solução contém 208 bombas. Aqui está uma solução possível (consegui resolver isso em cerca de 12 segundos).
Como uma maneira de testar os resultados que o Mathematica está produzindo, verifique se o seu algoritmo ganancioso pode melhorar.
fonte
Não há necessidade de transformar o problema em subproblemas lineares.
Em vez disso, use uma heurística simples e gananciosa, que é bombardear os cantos , começando pelo maior.
No exemplo dado, existem quatro cantos, {2, 1, 6, 4}. Para cada canto, não há movimento melhor do que bombardear a célula diagonal para o canto; portanto, sabemos de fato que nossos primeiros bombardeios 2 + 1 + 6 + 4 = 13 devem estar nessas células diagonais. Após o bombardeio, ficamos com uma nova matriz:
Após os primeiros 13 atentados, usamos a heurística para eliminar 3 0 2 por meio de três atentados. Agora, temos 2 novos cantos, {2, 1} na quarta linha. Nós bombardeamos esses, outros 3 atentados. Reduzimos a matriz para 4 x 4 agora. Há um canto, no canto superior esquerdo. Nós bombardeamos isso. Agora temos 2 cantos restantes, {5, 3}. Como 5 é o maior canto, bombardeamos aquele primeiro, 5 bombardeios e, finalmente, bombardeámos os 3 no outro canto. O total é 13 + 3 + 3 + 1 + 5 + 3 = 28.
fonte
Isso faz uma busca ampla pelo caminho mais curto (uma série de atentados) através desse "labirinto" de posições. Não, não posso provar que não há algoritmo mais rápido, desculpe.
fonte
Parece que uma abordagem de programação linear pode ser muito útil aqui.
Seja P m xn a matriz com os valores das posições:
Agora vamos definir uma matriz de bomba B (x, y) mxn , com 1 ≤ x ≤ m , 1 ≤ y ≤ n como abaixo
de tal maneira que
Por exemplo:
Então, estamos olhando para uma matriz B m xn = [ b ij ] que
Pode ser definido como uma soma de matrizes de bomba:
( q ij seria então a quantidade de bombas que cairíamos na posição p ij )
p ij - b ij ≤ 0 (para ser mais sucinto, digamos que P - B ≤ 0 )
Além disso, B deve minimizar a soma .
Também podemos escrever B como a matriz feia adiante:
e como P - B ≤ 0 (que significa P ≤ B ), temos o seguinte sistema de desigualdade bastante linear abaixo:
Sendo q mn x 1 definido como
p mn x 1 definido como
Podemos dizer que temos um sistema O sistema abaixo representado como produto de matrizes http://latex.codecogs.com/gif.download?S%5Cmathbf%7Bq%7D&space;%5Cge&space;%5Cmathbf%7Bp%7D sendo S mn x mn da matriz para ser revertidas para resolver o sistema. Eu não a expandi, mas acredito que deve ser fácil fazê-lo em código.
Agora, temos um problema mínimo que pode ser declarado como
Eu acredito que é algo fácil, quase trivial de ser resolvido com algo como o algoritmo simplex (existe um documento bastante interessante sobre isso ). No entanto, eu não conheço quase nenhuma programação linear (vou fazer um curso sobre isso no Coursera, mas é no futuro ...), tive algumas dores de cabeça tentando entender e tenho um trabalho freelance enorme para terminar, então apenas desista aqui. Pode ser que eu fiz algo errado em algum momento, ou que ele não pode ir mais longe, mas acredito que este caminho pode eventualmente levar à solução. De qualquer forma, estou ansioso pelo seu feedback.
(Agradecimentos especiais por este site incrível para criar imagens a partir de expressões LaTeX )
fonte
"Despite the many crucial applications of this problem, and intense interest by researchers, no efficient algorithm is known for it.
consulte a página 254. A programação linear inteira é um problema computacional muito difícil. Nossa única esperança de ser eficiente é explorar as propriedades intrínsecas sobre sua matriz de S. Não é que arbitrária depois de tudo.Esta solução gananciosa
parece estar correta:Como apontado nos comentários, ele falhará em 2D. Mas talvez você possa melhorá-lo.
Para 1D:
se houver pelo menos 2 números, você não precisará disparar para o número mais à esquerda, porque disparar para o segundo não é pior . Então atire para o segundo, enquanto o primeiro não é 0, porque você precisa fazer isso. Mover para a próxima célula. Não se esqueça da última célula.
Código C ++:
Então, para 2D:
Novamente: você não precisa filmar na primeira linha (se houver a segunda). Então atire para o segundo. Resolva a tarefa 1D da primeira linha. (porque você precisa torná-lo nulo). Descer. Não esqueça a última linha.
fonte
"0110","1110","1110"
. Você só precisa de um tiro, mas eu acredito que seu algoritmo usaria 2.Para minimizar o número de bombas, temos que maximizar o efeito de cada bomba. Para conseguir isso, a cada passo precisamos selecionar o melhor alvo. Para cada ponto que o soma e são oito vizinhos - poderia ser usado como uma quantidade eficiente de bombardeio nesse ponto. Isso fornecerá a seqüência quase ideal de bombas.
UPD : Também devemos levar em consideração o número de zeros, pois bombiná-los é ineficiente. De fato, o problema é minimizar o número de zeros atingidos. Mas não podemos saber como qualquer passo nos aproxima desse objetivo. Concordo com a ideia de que o problema está completo. Sugiro uma abordagem gananciosa, que dará uma resposta quase real.
fonte
1010101
,0010100
(linha superior, linha inferior) Sua abordagem exigirá 3. Pode ser feito em 2.Eu acredito que, para minimizar a quantidade de bombas, você simplesmente precisa maximizar a quantidade de dano. Para que isso aconteça, verifique a área que possui a força mais forte. é mais forte .. e bombardeie lá .. e faça até que o campo esteja plano .. para isso arquivado a resposta é 28
fonte
Aqui está uma solução que generaliza as boas propriedades dos cantos.
Vamos supor que possamos encontrar um ponto de queda perfeito para um determinado campo, ou seja, a melhor maneira de diminuir o valor nele. Então, para encontrar o número mínimo de bombas a serem descartadas, um primeiro rascunho de um algoritmo pode ser (o código é copiado e colado de uma implementação em ruby):
O desafio é
choose_a_perfect_drop_point
. Primeiro, vamos definir o que é um ponto de gota perfeito.(x, y)
queda para diminui o valor em(x, y)
. Também pode diminuir valores em outras células.(x, y)
é melhor que um ponto de queda b, pois(x, y)
se diminuir os valores em um superconjunto adequado das células que b diminui.(x, y)
são equivalentes se eles diminuírem o mesmo conjunto de células.(x, y)
é perfeito se for equivalente a todos os pontos de queda máximos para(x, y)
.Se houver um ponto de queda perfeito
(x, y)
, você não poderá diminuir o valor com(x, y)
mais eficiência do que soltar uma bomba em um dos pontos de queda perfeitos(x, y)
.Um ponto de descarte perfeito para um determinado campo é um ponto de descarte perfeito para qualquer uma de suas células.
Aqui estão alguns exemplos:
O ponto de queda perfeito para a célula
(0, 0)
(índice baseado em zero) é(1, 1)
. Todos os outros pontos de queda para(1, 1)
, ou seja(0, 0)
,(0, 1)
e(1, 0)
, diminuir menos células.Um ponto de gota perfeito para a célula
(2, 2)
(índice de base zero) é(2, 2)
, e também todas as células vizinhas(1, 1)
,(1, 2)
,(1, 3)
,(2, 1)
,(2, 3)
,(3, 1)
,(3, 2)
, e(3, 3)
.um ponto de queda perfeito para a célula
(2, 2)
é(3, 1)
: diminui o valor em(2, 2)
e o valor em(4, 0)
. Todos os outros pontos de queda para(2, 2)
não são máximos, pois diminuem uma célula a menos. O ponto de descarte perfeito para(2, 2)
também é o ponto de descarte perfeito para(4, 0)
e é o único ponto de descarte perfeito para o campo. Isso leva à solução perfeita para esse campo (uma gota de bomba).Não há ponto de queda perfeito para
(2, 2)
: Ambos(1, 1)
e(1, 3)
diminuir(2, 2)
e outra célula (são pontos de queda máximos para(2, 2)
), mas não são equivalentes. No entanto,(1, 1)
é um ponto de queda perfeito para(0, 0)
e(1, 3)
é um ponto de queda perfeito para(0, 4)
.Com essa definição de pontos de entrega perfeitos e uma certa ordem de verificações, obtenho o seguinte resultado para o exemplo da pergunta:
No entanto, o algoritmo funciona apenas se houver pelo menos um ponto de queda perfeito após cada etapa. É possível construir exemplos onde não há pontos de queda perfeitos:
Nesses casos, podemos modificar o algoritmo para que, em vez de um ponto de queda perfeito, escolhamos uma coordenada com uma escolha mínima de pontos de queda máximos e calculemos o mínimo para cada escolha. No caso acima, todas as células com valores têm dois pontos de queda máximos. Por exemplo,
(0, 1)
tem os pontos de queda máximos(1, 1)
e(1, 2)
. Escolher um deles e depois calcular o mínimo leva a esse resultado:fonte
Aqui está outra idéia:
Vamos começar atribuindo um peso a cada espaço no quadro para quantos números seriam reduzidos ao soltar uma bomba ali. Portanto, se o espaço tiver um número diferente de zero, ele receberá um ponto e, se qualquer espaço adjacente a ele tiver um número diferente de zero, receberá um ponto adicional. Portanto, se houver uma grade de 1000 por 1000, teremos um peso atribuído a cada um dos 1 milhão de espaços.
Em seguida, classifique a lista de espaços por peso e bombardeie aquele com o maior peso. Isso está recebendo o maior retorno possível, por assim dizer.
Depois disso, atualize o peso de cada espaço cujo peso seja afetado pela bomba. Este será o espaço que você bombardeou, e qualquer espaço imediatamente adjacente a ele e qualquer espaço imediatamente adjacente a eles. Em outras palavras, qualquer espaço que pudesse ter seu valor reduzido a zero pelo bombardeio ou o valor de um espaço vizinho reduzido a zero.
Em seguida, reordene os espaços da lista por peso. Como apenas um pequeno subconjunto de espaços teve seu peso alterado pelo bombardeio, você não precisará recorrer à lista inteira, basta movê-los pela lista.
Bombardeie o novo espaço de maior peso e repita o procedimento.
Isso garante que todo bombardeio reduz o maior número de espaços possível (basicamente, atinge o menor número possível de espaços que já são zero), então seria ótimo, exceto que eles podem ter laços de peso. Portanto, pode ser necessário rastrear as costas quando houver um empate para o peso máximo. Porém, apenas um empate para o peso máximo é importante, não outros empates, por isso espero que não seja muito para trás.
Edit: O contra-exemplo de Mysticial abaixo demonstra que, de fato, isso não é garantido como ideal, independentemente dos vínculos de peso. Em alguns casos, reduzir o peso o máximo possível em uma determinada etapa, na verdade, deixa as bombas restantes espalhadas demais para alcançar uma redução cumulativa tão alta após o segundo passo, como você poderia ter com uma escolha um pouco menos gananciosa no primeiro passo. Fiquei um tanto enganado com a noção de que os resultados são insensíveis à ordem dos atentados. Eles sãoinsensível à ordem, pois você pode pegar qualquer série de atentados e reproduzi-los desde o início em uma ordem diferente e acabar com o mesmo quadro resultante. Mas não decorre disso que você possa considerar cada atentado de forma independente. Ou, pelo menos, cada bombardeio deve ser considerado de uma maneira que leve em consideração o quão bem ele define o quadro para os bombardeios subsequentes.
fonte
1010101
,0010100
pode ser um contra-exemplo que comprova que essa abordagem não é ótima. Esta abordagem requer 3. Pode ser feito em 2.Bem, suponha que numeremos as posições 1, 2, ..., nx m. Qualquer sequência de descargas de bombas pode ser representada por uma sequência de números neste conjunto, onde os números podem se repetir. No entanto, o efeito no quadro é o mesmo, independentemente da ordem em que você solta as bombas; portanto, qualquer opção de descarte de bombas pode ser representada como uma lista de números nxm, em que o primeiro número representa o número de bombas lançadas na posição 1. , o segundo número representa o número de bombas lançadas na posição 2, etc. Vamos chamar esta lista de números nxm de "chave".
Você pode tentar primeiro calcular todos os estados da placa resultantes de uma queda de bomba, depois usá-los para calcular todos os estados da placa resultantes de duas descargas de bombas, etc., até obter todos os zeros. Porém, em cada etapa, você armazenaria em cache os estados usando a chave definida acima, para poder usar esses resultados no cálculo da próxima etapa (uma abordagem de "programação dinâmica").
Mas, dependendo do tamanho de n, me dos números na grade, os requisitos de memória dessa abordagem podem ser excessivos. Você pode jogar fora todos os resultados das descargas de N bombas depois de calcular todos os resultados para N + 1, para que haja algumas economias. E, é claro, você não pode armazenar em cache nada com o custo de demorar muito mais - a abordagem de programação dinâmica troca memória por velocidade.
fonte
Se você deseja que a solução ótima absoluta limpe a placa, terá que usar o retorno clássico, mas se a matriz for muito grande, levará séculos para encontrar a melhor solução, se você quiser uma solução ideal "possível", poderá usar o algoritmo ganancioso , se você precisar de ajuda para escrever o algoritmo, eu posso ajudá-lo
Venha para pensar que é o melhor caminho. Faça outra matriz lá, você armazena os pontos que remove, soltando uma bomba lá, em seguida, escolha a célula com o máximo de pontos e solte a bomba lá, atualize a matriz de pontos e continue. Exemplo:
valor da célula +1 para cada célula adjacente com um valor maior que 0
fonte
Força Bruta!
Sei que não é eficiente, mas mesmo que você encontre um algoritmo mais rápido, sempre poderá testar esse resultado para saber quão preciso é.
Use alguma recursão, como esta:
Você pode tornar isso mais eficiente armazenando em cache, se uma maneira diferente levar ao mesmo resultado, você não deve repetir as mesmas etapas.
Para elaborar:
se a célula de bombardeio 1,3,5 leva ao mesmo resultado que a célula de bombardeio 5,3,1, então você não deve refazer todas as próximas etapas novamente nos dois casos; apenas 1 é suficiente, você deve armazenar em algum lugar estados da tabela e use seus resultados.
Um hash de estatísticas da tabela pode ser usado para fazer uma comparação rápida.
fonte
Edit: não percebeu que Kostek sugeriu quase a mesma abordagem, então agora faço uma afirmação mais forte: se os cantos a serem limpos são escolhidos para estar sempre na camada mais externa, então é ideal.
No exemplo do OP: soltar 2 (como 1 + 1 ou 2) em qualquer outra coisa que não em 5 não leva a acertar qualquer quadrado que cair em 5 seria atingido. Então, simplesmente precisamos soltar 2 em 5 (e 6 na parte inferior esquerda 1 ...)
Depois disso, há apenas uma maneira de limpar (no canto superior esquerdo) o que era originalmente 1 (agora 0), e isso é deixar 0 em B3 (excel como notação). E assim por diante.
Somente depois de limpar colunas A e E inteiras e 1 e 7 linhas, comece a limpar uma camada mais fundo.
Considere limpar apenas os que foram intencionalmente limpos, limpar 0 cantos de valor não custa nada e simplifica o pensamento a respeito.
Como todas as bombas lançadas dessa maneira devem ser lançadas e isso leva a campos limpos, é a solução ideal.
Depois de dormir bem, percebi que isso não é verdade. Considerar
Minha abordagem lançaria bombas em B3 e C2, quando lançar em B2 seria suficiente
fonte
Aqui está minha solução. Ainda não escreverei no código, pois não tenho tempo, mas acredito que isso deve produzir um número ideal de movimentos a cada vez - embora não tenha certeza de quão eficiente seria encontrar os pontos a bombardear.
Em primeiro lugar, como @Luka Rahne afirmou em um dos comentários, a ordem na qual você bombardeia não é importante - apenas a combinação.
Em segundo lugar, como muitos outros afirmaram, bombardear 1-off a diagonal dos cantos é o ideal, pois atinge mais pontos do que os cantos.
Isso gera a base para a minha versão do algoritmo: podemos bombardear o '1-off dos cantos' primeiro ou último, não importa (em teoria) Nós bombardeamos os primeiros porque facilita as decisões posteriores (na prática) Bombardeamos o ponto que afeta mais pontos, ao mesmo tempo bombardeamos esses cantos.
Vamos definir Pontos de Resistência como os pontos do tabuleiro com os pontos mais não bombeáveis + o maior número de 0s ao seu redor
pontos não-bombeáveis podem ser definidos como pontos que não existem no nosso escopo atual do fórum que estamos analisando.
Também definirei 4 limites que manipularão nosso escopo: Superior = 0, Esquerdo = 0, Baixo = k, Direito = j. (valores a começar)
Finalmente, definirei bombas ótimas como bombas que são lançadas em pontos adjacentes a pontos de resistência e estão tocando (1) no ponto de resistência mais alto e (2) no maior número de pontos possível.
Com relação à abordagem, é óbvio que estamos trabalhando de fora para dentro. Poderemos trabalhar com 4 'bombardeiros' ao mesmo tempo.
Os primeiros pontos de resistência são obviamente nossos cantos. Os pontos 'fora do limite' não são bombeáveis (existem 5 pontos fora do escopo para cada canto). Então, bombardeamos os pontos na diagonal um dos cantos primeiro.
Algoritmo:
repita até TOP = INFERIOR e ESQUERDA = DIREITA
Vou tentar escrever o código atual mais tarde
fonte
Você poderia usar o planejamento do espaço de estado. Por exemplo, usando A * (ou uma de suas variantes) juntamente com uma heurística
f = g + h
como esta:fonte
Eu também tenho 28 jogadas. Eu usei dois testes para a melhor jogada seguinte: primeiro, a jogada que produz a soma mínima para o tabuleiro. Segundo, para somas iguais, o movimento que produz a densidade máxima, definido como:
Este é Haskell. "resolver placa" mostra a solução do mecanismo. Você pode jogar digitando "main" e, em seguida, insira um ponto de destino, "melhor" para uma recomendação ou "sair" para sair.
SAÍDA:
* Principal> resolver placa
[(4,4), (3,6), (3,3), (2,2), (2,2), (4,6), (4,6), (2,6), (3,2), (4,2), (2,6), (3,3), (4,3), (2,6), (4,2), (4) , 6), (4,6), (3,6), (2,6), (2,6), (2,4), (2,4), (2,6), (3,6) ), (4,2), (4,2), (4,2), (4,2)]
fonte
Parece haver uma subestrutura correspondente não bipartida aqui. Considere a seguinte instância:
A solução ideal para este gabinete tem tamanho 5, já que é o tamanho de uma cobertura mínima dos vértices de um ciclo de 9 pelas bordas.
Esse caso, em particular, mostra que o relaxamento da programação linear que algumas pessoas postaram não é exato, não funciona e todas essas outras coisas ruins. Tenho certeza de que posso reduzir "cobrir os vértices do meu gráfico cúbico planar o mínimo possível de arestas" "ao seu problema, o que me faz duvidar se alguma das soluções gananciosas / escaladas vai funcionar.
Não vejo uma maneira de resolver isso em tempo polinomial no pior dos casos. Pode haver uma solução muito inteligente de pesquisa binária e DP que não estou vendo.
EDIT : Vejo que o concurso ( http://deadline24.pl ) é independente de idioma; eles enviam vários arquivos de entrada e você envia a eles saídas. Portanto, você não precisa de algo que seja executado no pior momento polinomial. Em particular, você pode ver a entrada !
Existem vários casos pequenos na entrada. Depois, há um caso de 10x1000, um caso de 100x100 e um caso de 1000x1000. Os três casos grandes são todos muito bem-comportados. As entradas adjacentes horizontalmente normalmente têm o mesmo valor. Em uma máquina relativamente robusta, sou capaz de resolver todos os casos por força bruta usando o CPLEX em apenas alguns minutos. Eu tive sorte no 1000x1000; o relaxamento do LP tem uma solução ideal integral. Minhas soluções concordam com os
.ans
arquivos fornecidos no pacote de dados de teste.Aposto que você pode usar a estrutura da entrada de uma maneira muito mais direta do que eu, se você desse uma olhada; parece que você pode cortar a primeira linha, ou duas ou três repetidamente, até não ter mais nada. (Parece que no 1000x1000 todas as linhas não aumentam? Acho que é daí que vem a sua "parte B"?)
fonte
Não consigo pensar em uma maneira de calcular o número real sem apenas computar a campanha de bombardeio usando minha melhor heurística e espero obter um resultado razoável.
Portanto, meu método é calcular uma métrica de eficiência de bombardeio para cada célula, bombardear a célula com o valor mais alto, ... iterar o processo até que eu achatasse tudo. Alguns têm defendido o uso de dano potencial simples (ou seja, pontuação de 0 a 9) como métrica, mas isso fica aquém do impacto das células de alto valor e da não utilização de sobreposição de dano. Eu calcularia
cell value - sum of all neighbouring cells
, redefiniria qualquer positivo para 0 e usaria o valor absoluto de qualquer coisa negativa. Intuitivamente, essa métrica deve fazer uma seleção que ajude a maximizar a sobreposição de danos nas células com contagem alta, em vez de bater diretamente nas células.O código abaixo atinge a destruição total do campo de teste em 28 bombas (observe que o uso de dano potencial como métrica gera 31!).
O padrão de bombardeio resultante é produzido da seguinte forma (valores de campo à esquerda, métrica à direita)
fonte
Isso pode ser resolvido usando uma árvore de profundidade O (3 ^ (n)). Onde n é a soma de todos os quadrados.
Primeiro, considere que é trivial resolver o problema com uma árvore de O (9 ^ n), basta considerar todos os locais de bombardeio possíveis. Para um exemplo, consulte a implementação da Alfe .
Em seguida, perceba que podemos trabalhar para bombardear de baixo para cima e ainda obter um padrão mínimo de bombardeio.
Este algoritmo está correto porque
Na prática, esse algoritmo se sai regularmente melhor do que o máximo teórico, porque bombardeia regularmente os vizinhos e reduz o tamanho da pesquisa. Se assumirmos que cada bombardeio diminui o valor de 4 alvos adicionais, nosso algoritmo será executado em O (3 ^ (n / 4)) ou aproximadamente O (1,3 ^ n).
Como esse algoritmo ainda é exponencial, seria aconselhável limitar a profundidade da pesquisa. Podemos limitar o número de ramificações permitidas a algum número, X, e uma vez que estamos tão profundos, forçamos o algoritmo a escolher o melhor caminho que ele identificou até agora (aquele que possui a soma total mínima da placa em uma de suas folhas terminais ) Então, nosso algoritmo é garantido para executar em O (3 ^ X), mas não é garantido que você obtenha a resposta correta. Entretanto, sempre podemos aumentar o X e testar empiricamente se vale a pena a troca entre aumento da computação e melhores respostas.
fonte
função de avaliação, soma total:
função objetiva:
destruir a função:
função objetivo:
função de maximização linear:
Isso não é ideal, mas pode ser otimizado através da busca de uma melhor função de avaliação.
.. mas pensando nesse problema, eu estava pensando que um dos principais problemas é obter números abandonados no meio de zeros em algum momento, então eu adotaria outra abordagem .. que é dominar valores mínimos em zero e tentar zeros de escape possível, o que leva a uma minimização geral de valores mínimos mínimos, aproximadamente
fonte
Todo esse problema se resume a calcular uma distância de edição. Simplesmente calcule uma variante da distância de Levenshtein entre a matriz fornecida e a matriz zero, onde as edições são substituídas por bombardeios, usando programação dinâmica para armazenar as distâncias entre matrizes intermediárias. Sugiro usar um hash das matrizes como chave. No pseudo-Python:
fonte
Esta foi uma resposta para a primeira pergunta feita. Eu não tinha notado que ele mudou os parâmetros.
Crie uma lista de todos os destinos. Atribua um valor ao destino com base no número de valores positivos impactados por uma queda (ela própria e todos os vizinhos). O valor mais alto seria um nove.
Classifique os destinos pelo número de destinos impactados (Decrescente), com uma classificação descendente secundária na soma de cada destino impactado.
Solte uma bomba no alvo mais alto, depois recalcule os alvos e repita até que todos os valores sejam zero.
Concordado, isso nem sempre é o melhor. Por exemplo,
Esta abordagem levaria 5 bombas para limpar. Idealmente, você pode fazê-lo em 4. Ainda assim, bem perto e não há retorno. Para a maioria das situações, será ideal ou muito próximo.
Usando os números dos problemas originais, essa abordagem resolve em 28 bombas.
Adicionando código para demonstrar essa abordagem (usando um formulário com um botão):
Uma aula que você precisará:
fonte
09090
Essa abordagem requer 18 bombas. Isso pode ser feito em 9.1010101
,0010100
?