[Pergunta fácil diária] Pergunta de programação dinâmica de dificuldade difícil no Leetcode - Implementação do jogo de masmorra

Insira a descrição da imagem aqui

Página pessoal de Junxi_

Não importa o quão longe você vá, nunca se esqueça da sua intenção original quando iniciou a jornada.

Desenvolvimento de jogos C/C++

Olá, mineiros, aqui é Junxi_, um blogueiro que tem estudado algoritmos de programação dinâmica recentemente. Recentemente, encontrei uma questão de programação dinâmica de dificuldade difícil quando estava resolvendo questões no Leetcode. Hoje aproveitarei esta oportunidade para compartilhá-la com vocês. Aqui estão algumas das minhas opiniões e soluções para este tópico (não se preocupe, sou AC)

  • Ok, sem mais delongas, vamos começar nosso estudo hoje! !

jogos de masmorras

Insira a descrição da imagem aqui

Insira a descrição da imagem aqui

  • Ok, ok, não consigo entender o que significa quando olho para muitas palavras no título. Depois vejo o nível de dificuldade marcado acima. Muitas pessoas acreditam que, assim como o blogueiro, estão prontas para clicar primeiro. Não fique impaciente ainda. Deixe-me guiá-lo passo a passo para analisar o significado desta pergunta

Análise de perguntas

Insira a descrição da imagem aqui
(ps: Essa aqui é mesmo uma princesa dos quadrinhos)

  • Nossa princesa foi capturada e trancada no canto inferior direito, como mostra a foto
    Insira a descrição da imagem aqui
  • Em seguida, nosso cavaleiro deve começar da posição na imagem. Se ele encontrar demônios (ou seja, o valor na grade for negativo), ele precisará combatê-los e seu sangue será deduzido. Quando ele encontrar a bola mágica ( valor positivo na imagem), pode restaurar o sangue. Neste momento, a questão nos pergunta de quanto sangue o cavaleiro precisa pelo menos quando está na posição inicial (está estipulado que quando o volume de sangue em uma determinada posição for maior ou igual a 1, você pode passar, caso contrário você será reprovado )
  • Então, através da descrição do tema e combinada com as ideias de programação dinâmica que aprendemos antes, você encontra algo diferente? Como uma questão de dificuldade difícil, é definitivamente impossível resolvê-la usando o pensamento convencional. Bem, deixe-me levá-lo através de uma análise detalhada dos princípios do algoritmo.

Princípio do algoritmo

1. Exibição de status

  • Já dissemos no algoritmo de programação dinâmica que ao encontrar problemas de programação dinâmica, a solução geral é dividir em duas situações:
    • (1) Selecione uma determinada posição como ponto final, estabeleça uma tabela dp e exiba o status
    • (2) Selecione um determinado local como ponto de partida...
  • Segundo o pensamento convencional, como sabemos a localização da princesa, o normal é escolher a primeira situação para tentar representar o status.
  • Se seguirmos esta ideia e definirmos esta questão como: partindo do ponto inicial e atingindo a posição [i, j], o número mínimo de pontos de saúde iniciais necessários.
  • Então haverá um problema quando analisarmos a transferência de estado: ou seja, nossos atuais pontos de saúde também serão afetados pelos caminhos subsequentes. Isto é, a transferência estatal de cima para baixo não pode resolver bem o problema.

Por que? Vamos imaginar que a saúde do nosso cavaleiro está muito baixa neste momento. Seja voltado para baixo ou para a direita no próximo quadrado, encontraremos um demônio e a saúde do nosso cavaleiro será deduzida como um número negativo. O valor de dp aqui é razoável neste caso? tempo? É obviamente irracional. Portanto, em vez de considerar a situação que temos diante de nós, precisamos também considerar a situação do caminho que está atrás. Não é muito problemático?

  • Neste momento, precisamos mudar o estado para representar: a partir da posição [i, j], os pontos iniciais mínimos de saúde necessários para atingir o ponto final. Desta forma, quando analisamos a transição de estado, não precisamos considerar o caminho anterior, e o estado ótimo subsequente já é conhecido, o que simplifica muito a dificuldade da nossa análise.

  • Em resumo, o estado definido é expresso como:
    dp[i][j] significa: a partir da posição [i, j], o mínimo inicial necessário para atingir o ponto final Pontos de Saúde


2 Equação de transição de estado

  • Para dp[i][j], partindo da posição [i, j], existem duas opções para a próxima etapa (para facilitar o entendimento, deixe a resposta final de dp[i][j] ser x):

  • i. Vá para a direita e depois em direção à linha de chegada

  • Então nossos pontos de saúde mais baixos na posição [i, j] mais o consumo desta posição devem ser maiores ou iguais aos pontos de saúde mais baixos na posição correta, ou seja: x + masmorra[i][j ] >= dp[i][j + 1] .
    Ao mudar os termos, obtemos: x >= dp[i][j + 1] - dungeon[i][j] . Porque queremos o valor mínimo, neste caso x = dp[i][j + 1] - dungeon[i][j] ;

  • ii. Desça e vá até o final

  • Então nossos pontos de saúde mais baixos na posição [i, j] mais o consumo desta posição devem ser maiores ou iguais aos pontos de saúde mais baixos na posição inferior, ou seja: x + masmorra[i][j ] >= dp[i + 1][j] .
    Ao mudar os termos, obtemos: x >= dp[i + 1][j] - dungeon[i][j] . Porque queremos o valor mínimo, neste caso x = dp[i + 1][j] - dungeon[i][j] ;

  • Resumindo, o que precisamos é do valor mínimo nas duas situações, então a equação de transição de estado disponível é:
    dp[i][j] = min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j]

  • No entanto, se a masmorra[i][j] na posição atual for um número positivo relativamente grande, o valor de dp[i][j] pode se tornar 0 ou um número negativo. Ou seja, o ponto mais baixo será menor que 1, então o piloto morrerá. Portanto, se o dp[i][j] que encontramos for menor ou igual a 0, significa que o menor valor inicial neste momento deve ser 1. Para lidar com esta situação, você só precisa deixar dp[i][j] e 1 assumirem um valor máximo:
    dp[i][j] = max(1, dp[i][j])

O que isso significa? É [i,j] aqui que irá restaurar muito sangue, mas se o dp[i,j] neste momento for um número negativo, significa que o volume mínimo de sangue necessário para o cavaleiro aqui é 0 ou negativo número. Obviamente, isso não atende aos requisitos, portanto, precisamos lidar com esta situação especial conforme mencionado acima

inicialização

  • Você pode adicionar um "nó auxiliar" na frente para nos ajudar na inicialização. Há dois pontos a serem observados ao usar esta técnica:
  • i. O valor no nó auxiliar deve “garantir que o posterior preenchimento do formulário esteja correto”;
  • ii. "Mapeamento de relacionamento de subscritos".

O uso de nós auxiliares foi discutido no blog acima, então não entrarei em detalhes aqui.

  • Nesta questão, como precisamos considerar o impacto do caminho subsequente na posição atual, precisamos adicionar uma linha no final da tabela dp e, após adicionar uma coluna, todos os valores são primeiro inicializado até o infinito e então dp[m][n - 1] 或dp[m - 1][n] = 1 é o suficiente.

Ordem de preenchimento do formulário

  • De acordo com a "equação de transição de estado", precisamos "preencher cada linha de baixo para cima" e "preencher cada linha da direita para a esquerda". Depois de ler a análise do algoritmo acima, isso não deve ser difícil de entender.

valor de retorno

  • Como pode ser visto no título, nosso cavalo começa no canto superior esquerdo, então combinado com a análise acima, o valor que precisamos retornar é dp[0][0]

Escreva o código

class Solution {
    
    
public:
    int calculateMinimumHP(vector<vector<int>>& dungeon) {
    
    
        int m=dungeon.size();
        int n=dungeon[0].size();
        //建立dp表,以某个位置为开始建立状态转移方程
        vector<vector<int>> dp(m+1,vector<int>(n+1,INT_MAX));
        dp[m][n-1]=1;//考虑边界问题
        for(int i=m-1;i>=0;i--)
        {
    
    
            for(int j=n-1;j>=0;j--)
            {
    
    
            	//填表
                dp[i][j]=min(dp[i+1][j],dp[i][j+1])-dungeon[i][j];
                dp[i][j]=max(1,dp[i][j]);
            }
        }
        //返回值
        return dp[0][0];

    }
};
  • O código é muito simples, com apenas uma dúzia de linhas. O que é realmente difícil é o processo de análise do problema acima e o julgamento de algumas situações especiais. Acredito que se você consegue entender a análise dos princípios acima, isso não deveria ser um problema para você. Desastre.

Resumir

  • Ok, é isso por hoje! Na verdade, o código não é importante. É importante ser capaz de compreender os princípios ocultos e aprender a análise das questões correspondentes através desta questão. Portanto, se você quiser realmente aprendê-lo, você também pode tentar entender o princípios de algoritmo desde o início e depois escrever o código de forma independente.Desta forma, acredito que seja a melhor maneira de melhorar sua compreensão dos tópicos de programação dinâmica.
  • Se você tiver alguma dúvida ou dúvida sobre o conteúdo do artigo, fique à vontade para perguntar na área de comentários. Claro, você também pode me enviar uma mensagem privada e responderei o mais breve possível! !

Não é fácil para novos blogueiros criarem. Se você acha que o conteúdo do artigo é útil para você, leia-o três vezes seguidas antes de sair. Seu apoio é minha motivação para atualizar! ! !

**(Keli pede para você apoiar a blogueira três vezes seguidas!!! Clique nos comentários abaixo para curtir e coletar para ajudar Keli)**

Insira a descrição da imagem aqui

Acho que você gosta

Origin blog.csdn.net/syf666250/article/details/134796487
Recomendado
Clasificación