Multiestado simples de programação dinâmica

1. Massagista (fácil)

1. Link do tópico: Massagista
2. Descrição do tópico: Uma massagista famosa receberá um fluxo constante de solicitações de agendamento e você poderá optar por aceitar ou não cada agendamento. Há um intervalo entre cada consulta de serviço, portanto ela não pode aceitar consultas adjacentes. Dada uma sequência de solicitações de agendamento, encontre o agendamento ideal (o tempo total de agendamento mais longo) para o massagista e retorne o número total de minutos.
Exemplo 1:

Entrada: [1,2,3,1]
Saída: 4 Explicação: Selecione o compromisso 1 e o compromisso 3, duração total = 1 + 3 = 4.

Exemplo 2:

Entrada: [2,7,9,3,1]
Saída: 12 Explicação: Selecione Compromisso 1, Compromisso 3 e Compromisso 5, duração total = 2 + 9 + 1 = 12.

Exemplo 3:

Entrada: [2,1,4,5,3,1,1,3]
Saída: 12 Explicação: Selecione compromisso 1, compromisso 3, compromisso 5 e compromisso 8, duração total = 2 + 4 + 3 + 3 = 12.

3. Análise de problemas:
Para problemas como programação dinâmica com múltiplos estados, depois de ler o tópico, obviamente existem vários estados para uma determinada coisa;estado, e o que temos que fazer é expressar esses dois estados e então calcular o valor ele pode alcançar em diferentes estados. Finalmente, de acordo com o problema para encontrar o valor máximo ou mínimo.

  1. Representação de estado: primeiro use um dp[n] para expressar, e então você descobrirá que para uma determinada posição, o convidado nesta posição pode escolher ou não (se não escolher, pode estar descansando), então é óbvio usar um dp As tabelas não podem representar esses dois estados. Portanto, a representação do estado pode ser a seguinte:f[i] significa: quando a posição i é selecionada, nums[i] deve ser selecionado, e o tempo de compromisso mais longo neste momento; g[i] significa: quando a posição i é selecionada, nums[i] não está selecionado, este O tempo máximo de agendamento de .
  2. Equação de transição de estado: para a matriz f[i], quando a posição i é selecionada, nums[i] deve ser selecionado e o tempo de reserva mais longo neste momento, portanto, se a posição i for obrigatória, então i-1 não poderá ser selecionado novamente , acontece que g[i] significa que nums[i] não está selecionado. Neste momento, quando a reserva mais longa é feita, f[i]=nums[i] + g[i-1]; g[i] significa a posição de i. Se não for selecionado, então i-1 pode ser selecionado ou não, e o valor máximo dos dois é obtido, g[i]=max(f[i-1], g[i-1] ). Portanto, as representações estaduais sãof[i] = nums[i] + g[i - 1];g[i] = max(f[i - 1], g[i - 1]);
  3. Inicialização: Para f[i], a posição i é obrigatória, então f[0]=nums[0]; para g[i], a posição i não está selecionada, então g[0]=0 .
  4. Ordem de preenchimento do formulário: De acordo com a equação de transição de estado, da esquerda para a direita, os dois formulários são preenchidos juntos.
  5. Valor de retorno: De acordo com a representação estadual, deverá ser retornado max(f[n - 1], g[n - 1]).

código mostrado abaixo:

class Solution 
{
    
    
public:
    int massage(vector<int>& nums) 
    {
    
    
        int n = nums.size();
        //判断是否为空
        if (n == 0)
            return 0;
        vector<int> f(n), g(n);
        //初始化
        f[0] = nums[0];
        for (int i = 1; i < n; ++i)
        {
    
    
            f[i] = nums[i] + g[i - 1];
            g[i] = max(f[i - 1], g[i - 1]);
        }
        return max(f[n - 1], g[n - 1]);
    }
};

2. Roubo II (médio)

1. Link do tópico: Roubo de casa II
2. Descrição do tópico: Você é um ladrão profissional que planeja roubar casas ao longo da rua e há uma certa quantia de dinheiro escondida em cada cômodo. Todas as casas deste local estão dispostas em círculo, o que significa que a primeira e a última casa ficam uma ao lado da outra. Ao mesmo tempo, as casas adjacentes estão equipadas com sistemas anti-roubo interligados.Se duas casas adjacentes forem arrombadas por ladrões na mesma noite, o sistema chamará automaticamente a polícia.
Dada uma série de números inteiros não negativos que representam a quantidade de dinheiro armazenada em cada casa, calcule a quantia máxima que você pode roubar esta noite sem disparar o alarme.

Exemplo 1:

Entrada: nums = [2,3,2]
Saída: 3 Explicação: Você não pode primeiro roubar a casa 1 (quantia = 2) e depois roubar a casa 3 (quantidade = 2), porque elas são adjacentes.

Exemplo 2:

Entrada: nums = [1,2,3,1]
Saída: 4 Explicação: Você pode primeiro roubar a casa 1 (dinheiro = 1) e depois roubar a casa 3 (dinheiro = 3). Quantidade máxima roubada = 1 + 3 = 4.

Exemplo 3:

Entrada: nums = [1,2,3]
Saída: 3

3. Análise do problema:
A diferença entre esta questão e a questão anterior é que o array nums é considerado um array circular. Neste caso, a seção crítica precisa ser considerada, ou seja, a situação no início e no final é analisados; os tipos específicos são os seguintes: Se a primeira Se uma posição for selecionada, então a última posição não pode ser selecionada, ou seja, no intervalo [0,n - 2]; se a última posição for selecionada, então a primeira a posição não pode ser selecionada, ou seja, o intervalo [1,n - 1] . Existe alguma outra situação? não sobrou nenhum. Então, 两个区间[0,n - 2]和[1,n - 1]o valor máximo pode ser obtido realizando o problema do massagista uma vez, respectivamente.

  1. Status significa:f[i] significa: quando a posição i é selecionada, nums[i] deve ser selecionado, e o lucro máximo é roubado neste momento; g[i] significa: quando a posição i é selecionada, nums[i] não é selecionado, e isso ao roubar o lucro máximo.
  2. Equação de transição de estado: tal como o método de análise do massagista, a equação de transição de estado é a seguinte:f[i] = nums[i] + g[i - 1];g[i] = max(f[i - 1], g[i - 1]);
  3. Inicialização: Para f[i], a posição i é obrigatória, então f[0]=nums[0]; para g[i], a posição i não está selecionada, então g[0]=0 .
  4. Ordem de preenchimento do formulário: De acordo com a equação de transição de estado, da esquerda para a direita, os dois formulários são preenchidos juntos.
  5. Valor de retorno: De acordo com a representação estadual, deverá ser retornado max(f[n - 1], g[n - 1]).

código mostrado abaixo:

class Solution 
{
    
    
public:
    int _rob(vector<int>& nums, int left, int right)
    {
    
    
        if (left > right)
            return 0;
        int n = nums.size();
        vector<int> f(n), g(n);
        //初始化第一个值
        f[left] = nums[left];
        for (int i = left + 1; i <= right; ++i)
        {
    
    
            f[i] = nums[i] + g[i - 1];
            g[i] = max(f[i - 1], g[i - 1]);
        }
        return max(f[right], g[right]);
    }
    int rob(vector<int>& nums) 
    {
    
    
        int n = nums.size();
        //判断临界条件
        if (n == 1)
            return nums[0];
        //分别求出两个区间[0,n - 2]和[1,n - 1]的最大值
        return max(_rob(nums, 0, n - 2), _rob(nums, 1, n - 1));
    }
};

3. Exclua e ganhe pontos (médio)

1. Link do tópico: Excluir e obter pontos
2. Descrição do tópico:
Você recebe um array de inteiros nums e pode realizar algumas operações nele.
Em cada operação, selecione qualquer um de nums[i], exclua-o e obtenha os pontos de nums[i]. Depois, você deve remover todos nums[i] - 1 和 nums[i] + 1 os elementos iguais.
Você começa com 0 pontos. Retorna o número máximo de pontos que você pode ganhar através destas operações.

Exemplo 1:

Entrada: nums = [3,4,2]
Saída: 6 Explicação: Excluir 4 obtém 4 pontos, então 3 também é excluído. Depois disso, remova 2 para 2 pontos. Obtenha 6 pontos no total.

Exemplo 2:

Entrada: nums = [2,2,3,3,3,4]
Saída: 9 Explicação: Exclua 3 para obter 3 pontos e, em seguida, exclua dois 2 e 4. Depois disso, remova 3 novamente para obter 3 pontos e remova 3 novamente para obter 3 pontos. Um total de 9 pontos são atribuídos.

dica:

1 <= nums.comprimento <= 2 * 10^4
1 <= nums[i] <= 10^4

3. Análise do problema:
Esta questão requer uma boa capacidade de compreensão. Provavelmente significa que você recebe uma matriz e então pode excluir um elemento e obter seu tamanho (aqui chamado de pontos), mas se você excluir um determinado número, como 5, então você não pode mais excluir 3 e 4 na matriz (a área vermelha acima) . O que vem à mente quando você vê isso? ? É quase igual à primeira pergunta acima?Se a posição i for obrigatória, as posições i-1 e i+1 não poderão ser selecionadas novamente. Então, algumas operações são necessárias para transformar este problema na mesma solução do problema acima. A ideia é a seguinte: No prompt, podemos saber que 1 <= nums[i] <= 10^4, então use uma matriz de Tamanho 10001 para gerenciar os dados, o subscrito corresponde a cada dado e o elemento armazenado representa a soma total do subscrito (por exemplo, se 4 aparecer 3 vezes, então os dados armazenados na posição 4 são 12).

  1. Representação de status: como na pergunta anterior,Use f[i] para indicar o número máximo de pontos obtidos quando a posição i for excluída; g[i] indica o número máximo de pontos obtidos se a posição i não for excluída
  2. Equação de transição de estado: tal como o método de análise do massagista, a equação de transição de estado é a seguinte:f[i] = hash[i] + g[i - 1];g[i] = max(f[i - 1], g[i - 1]);
  3. Inicialização: Para a posição f[0], o valor é 0 após seleção obrigatória.
  4. Ordem de preenchimento do formulário: da esquerda para a direita, preencha os dois formulários ao mesmo tempo.
  5. Valor de retorno: max(f[N - 1], g[N - 1]).

código mostrado abaixo:

class Solution 
{
    
    
public:
    int deleteAndEarn(vector<int>& nums) 
    {
    
    
        const int N = 10001;
        int hash[N] = {
    
     0 };
        for (auto& x : nums)
            hash[x] += x;
        
        vector<int> f(N), g(N);
        for (int i = 1; i < N; ++i)
        {
    
    
            f[i] = hash[i] + g[i - 1];
            g[i] = max(f[i - 1], g[i - 1]);
        }
        return max(f[N - 1], g[N - 1]);
    }
};

4. O melhor momento para comprar e vender ações inclui um período de congelamento (médio)

1. Link do título: O melhor momento para comprar e vender ações inclui um período de congelamento
2. Descrição do título: Dada uma matriz de preços inteiros, onde preços[i] representa o preço das ações no i-ésimo dia. Projete
um algoritmo para calcular o lucro máximo. Você pode concluir tantas transações quanto possível (comprar e vender uma ação várias vezes), sujeito às seguintes restrições:

Depois de vender as ações, você não poderá comprar as ações no dia seguinte (ou seja, o período de congelamento é de 1 dia).
Nota: Você não pode participar de múltiplas transações ao mesmo tempo (você deve vender o estoque anterior antes de comprar novamente).

Exemplo 1:

Entrada: preços = [1,2,3,0,2]
Saída: 3 Explicação: O status da transação correspondente é: [compra, venda, período de congelamento, compra, venda]

Exemplo 2:

Entrada: preços = [1]
Saída: 0

dica:

1 <= preços.comprimento <= 5000
0 <= preços[i] <= 1000

3. Análise do problema:
Este tipo de questão é primeiro dividido em estados e depois cada estado é discutido detalhadamente. Como deve ser dividido o status desta questão? Em um determinado dia, o status da transação pode serCompra, período de congelamento, após o período de congelamento, estará em estado negociável(Você precisa descobrir o status de negociação sozinho) Como o lucro no momento da venda é o mesmo que entrar no período de congelamento, então você não precisa se preocupar com o status de venda ao comprar e vender ações (se você considerar o status de venda, lembre-se que o lucro no período de congelamento é O lucro é igual ao do status de venda do dia).

  1. Representação de estado: Como existem três estados de “compra”, “negociável” e “período de congelamento”, podemos optar pela utilização de três arrays, entre os quais: dp[0][j] 表⽰:第 i 天结束后,处于「买⼊」状态,此时的最⼤利润;dp[1][j] 表⽰:第 i 天结束后,处于「冷冻期」状态,此时的最⼤利润;dp[2][j] 表⽰:第 i 天结束后,处于「可交易」状态,此时的最⼤利润.
  2. Equação de transição de estado: analisada pela figura a seguir:
    insira a descrição da imagem aqui

O final da seta indica ontem e a extremidade frontal da seta indica hoje. nada significa nada para fazer. Comece a análise a partir do estado de compra : você pode chegar ao estado de compra a partir da compra? Simplesmente não fazer nada, desde a compra até o período de congelamento? Foi vendido ontem, mas hoje é o período de congelamento, da compra à negociação? Após a compra, só poderá ser vendido até o período de congelamento, portanto não é viável. Status do período de congelamento : de período de congelamento a período de congelamento? Não, do período de congelamento para a compra, obviamente não, do período de congelamento para o negociável? Durante o período de congelamento de ontem, se você não fez nada, poderá ser negociado hoje. Status negociável : de negociável para negociável? Você não fez nada ontem no estado negociável e hoje está novamente no estado negociável, de negociável para compra? Foi negociável ontem, e claro que você pode comprá-lo hoje, do período negociável ao período congelado? não.
Portanto, a equação de transição de estado é a seguinte:dp[0][j] = max(dp[0][j - 1], dp[2][j - 1] - prices[j]); dp[1][j] = dp[0][j - 1] + prices[j]; dp[2][j] = max(dp[2][j - 1], dp[1][j - 1]);

  1. Inicialização: Todos os três estados usarão o valor da posição anterior, portanto a primeira posição de cada linha precisa ser inicializada:
    dp[0][0] : Se você quiser estar no estado "comprar" neste momento, você deve definir a primeira posição O estoque do dia é comprado, então dp[0][0] = -prices[0];
    dp[1][0]: nada precisa ser feito, então dp[1][0] = 0 ;
    dp[ 2][0]: Não há estoque em mãos e está em período de congelamento após a compra e venda. Neste momento o rendimento é 0, então dp[2][0] = 0.
  2. Ordem de preenchimento dos formulários: de cima para baixo, da esquerda para a direita, preencha os três formulários juntos.
  3. Valor de retorno: O lucro no estado de compra definitivamente não é o maior, então retorne o valor máximo no estado congelado ou no estado negociável.

código mostrado abaixo:

class Solution 
{
    
    
public:
    int maxProfit(vector<int>& prices) 
    {
    
    
        int n = prices.size();
        vector<vector<int>> dp(3, vector<int>(n));
        dp[0][0] = -prices[0];
        for (int j = 1; j < n; ++j)
        {
    
    
            dp[0][j] = max(dp[0][j - 1], dp[2][j - 1] - prices[j]);
            dp[1][j] = dp[0][j - 1] + prices[j];
            dp[2][j] = max(dp[2][j - 1], dp[1][j - 1]);
        }
        return max(dp[1][n - 1], dp[2][n - 1]);
    }
};

5. O melhor momento para comprar e vender ações III (difícil)

1. Link do tópico: O melhor momento para comprar e vender ações III
2. Descrição do tópico: Dado um array, seu i-ésimo elemento é o preço de uma determinada ação no dia i.
Projete um algoritmo para calcular o lucro máximo que você pode obter.Você pode concluir até duas (k) transações.

Nota: Você não pode participar de múltiplas transações ao mesmo tempo (você deve vender o estoque anterior antes de comprar novamente).

Exemplo 1:

Entrada: preços = [3,3,5,0,0,3,1,4]
Saída: 6 Explicação: compre no dia 4 (preço da ação = 0), compre no dia 6 (preço da ação = 3) ao vender, esta transação pode gerar lucro = 3-0 = 3.
Então, comprando no 7º dia (preço da ação = 1) e vendendo no 8º dia (preço da ação = 4), esta transação pode gerar lucro = 4-1 = 3.

Exemplo 2:

Entrada: preços = [1,2,3,4,5]
Saída: 4 Explicação: Compre no dia 1 (preço da ação = 1), venda no dia 5 (preço da ação = 5), esta transação pode gerar lucro = 5 -1 = 4.
Observe que você não pode comprar ações consecutivas no Dia 1 e no Dia 2 e vendê-las mais tarde.
Como isso envolve várias transações ao mesmo tempo, você deve vender as ações anteriores antes de comprar novamente.

Exemplo 3:

Entrada: preços = [7,6,4,3,1]
Saída: 0
Explicação: Neste caso, nenhuma transação é concluída, então o lucro máximo é 0.

Exemplo 4:

Entrada: preços = [1]
Saída: 0

dica:

1 <= preços.comprimento <= 10^5
0 <= preços[i] <= 10^5

3. Análise do problema:
Comparada com a questão anterior, esta questão precisa considerar mais um número de transações, por isso é necessário adicionar outra dimensão para representar o número de transações. Para o estado, existem apenas o estado de compra e o estado negociável, então duas matrizes de f e g são usadas para representá-lo, e a segunda dimensão j representa o número de transações.

  1. Status significa:f[i][j] 表⽰:第 i 天结束后,完成了 j 次交易,处于「买⼊」状态,此时的最⼤利润; g[i][j] 表⽰:第 i 天结束后,完成了 j 次交易,处于「卖出」状态,此时的最⼤利润。
  2. Equação de transição de estado:
    Para f[i][j], existem duas situações para atingir este estado:
    no dia i - 1, a transação é j vezes, e está no estado "compra", e nada é feito no dia eu. Posso. Neste momento, o lucro máximo é: f[i - 1][j]; no dia i - 1, a ação foi negociada j vezes e está no estado "venda", e a ação é comprada no dia i. O lucro máximo neste momento é: g[i - 1][j] - preços[i] . Em resumo, o que se busca é o “lucro máximo”, portanto é o máximo dos dois: f[i][j] = max(f[i - 1][j], g[i - 1][j ] - preços[i]) .
    Para g[i][j], existem duas situações para atingir esse estado:
    no dia i - 1, a transação é j vezes, e está no estado "venda", e nada é feito no dia i. O lucro máximo neste momento é: g[i - 1][j]; no dia i - 1, negociou j - 1 vezes, no estado "compra", vendeu o estoque no dia i
    e, em seguida está completo. O lucro máximo neste momento é: f[i - 1][j - 1] + preços[i] . Mas este estado não existe necessariamente, deve ser julgado primeiro. O que é necessário é o lucro máximo, então a equação de transição de estado é:g[i][j] = g[i - 1][j]; if(j >= 1) g[i][j] = max(g[i][j], f[i - 1][j - 1] + prices[i]);
  3. Inicialização:
    Como o estado em que i = 0 é necessário, basta inicializar a primeira linha.
    Quando for no dia 0, só pode estar no estado "comprar uma vez", e a receita neste momento é -preços[0], então f[0][0] = -preços[0].
    Para aproveitar o máximo, alguns estados inexistentes "não desempenham o papel de interferência", todos nós os inicializamos em -INF (há um risco de "estouro" no processo de cálculo com INT_MIN, aqui INF leva 0x3f3f3f3f pela metade , que é pequeno o suficiente) Situação de estouro: quando inicializado no infinito negativo, -prices[0]-oo irá transbordar.
  4. Ordem de preenchimento do formulário: preencha cada linha de cima para baixo, cada linha da esquerda para a direita e preencha os dois formulários juntos.
  5. Valor de retorno: retorna o valor máximo de cada transação.

código mostrado abaixo:

class Solution {
    
    
public:
    const int INF = 0x3f3f3f3f;
    int maxProfit(vector<int>& prices) {
    
    
        int n = prices.size();
        vector<vector<int>> f(n, vector<int>(3, -INF)), g(n, vector<int>(3, -INF));
        f[0][0] = -prices[0], g[0][0] = 0;
        for (int i = 1; i < n; ++i) 
        {
    
    
            for (int j = 0; j < 3; ++j)
            {
    
    
                f[i][j] = max(f[i - 1][j], g[i - 1][j] - prices[i]);
                if (j >= 1) //防止越界
                    g[i][j] = max(g[i - 1][j], f[i - 1][j - 1] + prices[i]);
                else 
                    g[i][j] = g[i - 1][j];
            }
        }
        int ret = 0;
        for (int j = 0; j < 3; ++j)
            ret = max(ret, g[n - 1][j]);
        return ret;
   }      
 } ;

Para obter o melhor momento para comprar e vender ações, problema IV, negociando k vezes, o método de análise é basicamente o mesmo do problema anterior, basta alterar 3 no código acima para k.

Acho que você gosta

Origin blog.csdn.net/weixin_68278653/article/details/132689703
Recomendado
Clasificación