contente
1. Empurrando uma porta a partir de um algoritmo recursivo puro
3. Mais alguns exercícios de definição de estado
1. Empurrando uma porta a partir de um algoritmo recursivo puro
O processo de processamento de problemas da classe recursiva:
- Definição do estado de recursão (núcleo, determina a fórmula de recursão) f[x] : expressão simbólica, mais a definição desta expressão sub
- Determine a fórmula de recorrência (k i-----> k i+1) Determine de qual f[y] f[x] depende
- Inicializar init (estado inicial recursivo)
- Implementação de programa em loop ou recursivo
Analise o tópico como acima:
Suponha que você esteja subindo escadas. Você precisa
n
de passos para chegar ao topo.Você pode subir um
1
ou2
mais degraus de cada vez. De quantas maneiras diferentes você pode chegar ao topo de um edifício?
- Definição do estado: dp[i] : o número de maneiras de subir as escadas i
- Equação de Transição de Estado: Analisando Dependências? Você pode subir 1 ou 2 degraus de cada vez, então, quando estiver nos degraus i, seu último estado pode ser nos degraus i - 1 ou i - 2 degraus na escada. Transferência gráfica:
- init : f0] = 1; //Existe apenas um método, não subiu no início, f[1] = 1 sobe do nível 0 para o nível 1
class Solution {
public:
int climbStairs(int n) {
int f[n + 1];
//init:
f[0] = 1; f[1] = 1;
for (int i = 2; i <= n; ++i) {
f[i] = f[i - 1] + f[i - 2];
}
return f[n];
}
};
2. Qual é a relação entre programação dinâmica e algoritmo recursivo, e por que a programação dinâmica é um algoritmo recursivo especial?
Conforme mostrado na figura acima: A regularização dinâmica deve ser um problema recursivo, mas um problema recursivo não é necessariamente uma programação dinâmica
- A programação dinâmica é um problema de tomada de decisão.... É um algoritmo recursivo especial para tomar decisões ótimas em um estado....
- Aqui, vamos começar com uma análise do problema recursivo acima e um tópico semelhante.
746. Suba escadas com custo mínimo
Você recebe uma matriz de inteiros
cost
, ondecost[i]
éi
o custo para subir o primeiro degrau da escada. Depois de pagar essa taxa, você pode optar por subir um ou dois degraus.Você pode optar por começar a subir as escadas a partir dos degraus subscritos
0
ou subscritos .1
Por favor, calcule e devolva o custo mínimo para chegar ao topo das escadas.
- Acontece que a programação dinâmica é um problema especial de recursão. Naturalmente, deve haver uma definição de estado. A definição de estado é o núcleo da resolução de programação dinâmica. Uma definição de estado bem definida tornará a equação de transição de estado da programação dinâmica muito mais simples. .. (mais tarde A sequência explicará um por um)
- Definir estado: dp[i] : custo mínimo para subir i degraus
- Análise de transição de estado:
- inicial: dp[0] = 0; dp[1] = 0; iniciar escadas, sem custo:
class Solution { public: int minCostClimbingStairs(vector<int>& cost) { int n = cost.size(); int dp[n + 1]; //init dp[0] = 0; dp[1] = 0; for (int i = 2; i <= n; ++i) { dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]); //决策 } return dp[n]; } };
Para resumir a análise da rotina de programação dinâmica:
- Definição de estado: expressão simbólica dp[i], mais explicação de significado
- A equação de transição de estado é determinada. (Processo de decisão de estado) A decisão é ótima
- init: inicializa o estado inicial
- Indução matemática para julgar se a equação de transferência está correta (iniciante sem gosto, útil para especialistas, verifique)
3. A programação dinâmica continua estudando a definição de estado e transição de estado Percorra o tópico para ver o processo de definição e transição e, a propósito, leve à árvore de estados. . . .
A espada refere-se à Oferta II 100. Soma dos menores caminhos em um triângulo
Dado um triângulo
triangle
, encontre a soma mínima do caminho de cima para baixo.Cada etapa só pode se mover para nós adjacentes na próxima linha. Nós adjacentes aqui se referem a dois nós cujo subscrito é igual ao subscrito do nó na camada anterior ou igual ao subscrito do nó na camada anterior + 1 . Ou seja, se você estiver no subscrito da linha atual , a próxima etapa poderá passar para o subscrito ou da próxima linha .
i
i
i + 1
- Ideias gráficas:
- init : dp[0][0] = custo[0][0];
- 转移方程 : dp[i][j] = min(dp[i - 1][j] + custo[i][j], dp[i - 1][j - 1] + custo[i][j] );
class Solution { public: int minimumTotal(vector<vector<int>>& cost) { int n = cost.size(); int dp[n][n]; //init dp[0][0] = cost[0][0]; for (int i = 1; i < n; ++i) { //行 for (int j = 0; j <= i; ++j) { if (j == 0) dp[i][j] = dp[i - 1][j] + cost[i][j]; else if (j == i) dp[i][j] = dp[i - 1][j - 1] + cost[i][j]; else { dp[i][j] = min(dp[i - 1][j], dp[i - 1][j - 1] ) + cost[i][j]; //状态抉择 } } } int ans = 0x3f3f3f3f; for (int i = 0; i < n; ++i) { ans = min(ans, dp[n - 1][i]); //从最下面一层中抉择结果 } return ans; } };
Estado Redefinido: Dê uma olhada nas vantagens da definição de estado:
-
dp[i][j] : a soma das distâncias mais curtas da camada inferior ao ponto (i, j)
-
init : dp[n - 1][i] : todos os pontos na camada inferior
-
Equação de transição: dp[i][j] = min(dp[i + 1][j], dp[i + 1][j + 1]) + custo[i][j];
-
Resumo das vantagens das definições acima: Na definição de reverso, o comprimento da estrada é o mesmo para frente e ré, mas o julgamento de casos especiais é excluído.
class Solution { public: int minimumTotal(vector<vector<int>>& cost) { int n = cost.size(); int dp[n][n]; //init for (int i = 0; i < n; ++i) { dp[n - 1][i] = cost[n - 1][i]; } for (int i = n - 2; i >= 0; --i) { for (int j = 0; j <= i; ++j) { dp[i][j] = min(dp[i + 1][j], dp[i + 1][j + 1]) + cost[i][j]; } } return dp[0][0]; } };
3. Mais alguns exercícios de definição de estado
Você é um ladrão profissional planejando roubar casas ao longo da rua. Há uma certa quantia de dinheiro escondida em cada quarto. A única restrição que afeta seu roubo é que as casas adjacentes estão equipadas com sistemas antifurto interligados. Se duas casas adjacentes forem arrombadas por ladrões na mesma noite, o sistema alarme automaticamente .
Dada uma matriz de inteiros não negativos representando a quantidade armazenada em cada casa, calcule a quantidade máxima que você pode roubar durante a noite sem acionar o alarme .
- Definição do estado: dp[i] : A quantidade máxima de dinheiro que pode ser roubada roubando a casa anterior sem roubar materiais adjacentes
- A definição do estado acima está correta????? Existe falta de informação????? Existe também uma restrição de que materiais adjacentes não podem ser roubados. Portanto, roubar a i-ésima sala e não roubar a i-th quarto são dois estados. . . . (chave principal de definição de estado 2: inclua todos os estados)
- dp[i][j] (j = 0 ou 1 ): dp[i][0] : representa a quantidade máxima que pode ser obtida sem roubar a i-ésima sala, dp[i][1] : representa roubar a i-ésima sala Quantidade máxima que pode ser obtida
- Transição de estado: dp[i][0] = max(dp[i - 1][1], dp[i - 1][0]); você pode roubar a i - 1ª casa sem roubar a ith casa, (Nota : sim você pode)
- dp[i][1] = dp[i - 1][0] + nums[i - 1] O valor máximo para roubar a i-ésima casa, que só pode ser transferida de dp[i - 1][0] , porque roubando Se a ª casa for encontrada, então a ª - 1ª casa deve estar em um estado que não tenha sido roubado (as casas consecutivas não podem ser roubadas ao mesmo tempo, e a polícia será chamada)
class Solution {
public:
int rob(vector<int>& nums) {
//状态定义: 还是思考最重要的问题?? 状态如何定义?????
// dp[i] ???? 足够吗?? 我们尝试一下定义: 前i家物资 最后第i 家偷盗的最大偷盗金额??
// 好像不够呀: 因为 这个转移可能是从 前面的 i - 1 不偷转移过来. 也可以是 i - 1偷转移过来. 这样一分析: 中间还需要保存 不偷的结果呀:
//所以这样一想还是需要二维的定义:
//dp[i][0] dp[i][1] 就是前面的i个房间 第 i 个房间 不偷和偷的最大偷盗金额
int n = nums.size();
int dp[n + 1][2];
dp[0][0] = dp[0][1] = 0; //没有房间一定是0结果 init
for (int i = 1; i <= n; ++i) {
dp[i][0] = max(dp[i - 1][1], dp[i - 1][0]);
dp[i][1] = dp[i - 1][0] + nums[i - 1]; //只能是前一个房子不偷
}
return max(dp[n][0], dp[n][1]);
}
};
53. Máximo de sub-matrizes e oferta da espada II 091. Pinte a casa
Se houver uma fileira de casas
n
e cada casa puder ser pintada em uma das três cores, vermelho, azul ou verde, você precisará pintar todas as casas e certificar-se de que as duas casas adjacentes não podem ser da mesma cor.Claro, porque os preços das diferentes cores de tinta no mercado são diferentes, o custo de pintar a casa em cores diferentes também é diferente. O custo de pintar cada casa de uma cor diferente é representado por uma
n x 3
matriz de inteiros positivoscosts
.Por exemplo,
costs[0][0]
representa o custo de pintar a casa 0 de vermelho;costs[1][2]
representa o custo de pintar a casa 1 de verde e assim por diante.Por favor, calcule o custo mínimo para pintar todas as casas.
- Definição de estado: Igual à pergunta anterior, a chave principal é que a definição de estado precisa incluir todas as situações, e cores diferentes são estados diferentes, portanto, é necessária uma matriz bidimensional e a segunda dimensão identifica a cor
- dp[i][j] (j = 0, 1, 2) : ou seja, o custo mínimo de tingir a primeira i casa e a última i casa com a cor j
- Transferência de estado: a chave central, a última cor i tingida i - 1 não pode ser tingida, então dp[i][j] depende do dp[i - 1][k] anterior ( k != j)
O acima ainda é relativamente abstrato e, em seguida, é fácil de entender observando o código:
class Solution {
public:
int minCost(vector<vector<int>>& costs) {
//定义状态: dp[i][j] : 前 i 个房子 最后一个粉刷 j 颜色的最小花费
//依赖关系: dp[i][j]----> dp[i - 1][k] k != j
int n = costs.size();
int dp[n + 1][3];
for (int i = 0; i < 3; ++i) { //init没有一栋屋子染色
dp[0][i] = 0;
}
for (int i = 1; i <= n; ++i) { //枚举屋子进行状态决策
dp[i][0] = min(dp[i - 1][1] + costs[i - 1][1],
dp[i - 1][2] + costs[i - 1][2]);
dp[i][1] = min(dp[i - 1][0] + costs[i - 1][0],
dp[i - 1][2] + costs[i - 1][2]);
dp[i][2] = min(dp[i - 1][1] + costs[i - 1][1],
dp[i - 1][0] + costs[i - 1][0]);
}
return min(dp[n][0], min(dp[n][1], dp[n][2]));
}
};
Insira um array inteiro, um ou mais inteiros consecutivos no array formam um subarray. Encontre o valor máximo da soma de todos os subarranjos.
A complexidade de tempo necessária é O(n).
- Definição do estado: dp[i] : o valor máximo da soma dos subarrays até o índice i
- Transição de estado: contínuo ou desconectado de um novo início ..... (O subarray contínuo é o maior, desde que o subarray contínuo não tenha se tornado um número negativo, ele pode continuar sendo contínuo , porque pode ser um post -sequence a matriz contínua antes que se torne um número negativo Torne-se maior e forneça contribuição, a menos que < 0 possa ser desconectado, você só pode tornar o todo cada vez menor, )
- Estado de dependência: dp[i - 1];
- Equação de transição de estado: dp[i] = max(dp[i - 1] + nums[i], nums[i]);
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int n = nums.size();
int dp[n];
dp[0] = nums[0];//init
int ans = dp[0];
for (int i = 1; i < n; ++i) {
dp[i] = max(dp[i - 1] + nums[i], nums[i]);//状态抉择, 连续或者断开
ans = max(ans, dp[i]);
}
return ans;
}
};
152. Subarray Máximo do Produto
Dado um array inteiro
nums
, encontre o subarray contíguo não vazio com o maior produto no array (o subarray contém pelo menos um número) e retorne o produto correspondente ao subarray.A resposta para o caso de teste é um inteiro de 32 bits .
Um subarray é uma subsequência contígua de um array.
- Definição de estado: dp[i][0] e dp[i][1] : respectivamente contêm os produtos mínimo e máximo das sub-matrizes das primeiras i tuplas
- Análise da equação de transição de estado : porque pode haver um número negativo muito pequeno * um número negativo torna todo o produto do subarray contínuo muito grande, então precisamos salvar o maior produto do subarray contínuo e o menor produto do subarray contínuo em todo o processo. O produto de arrays, então um array bidimensional é criado, uma dimensão contém o produto mínimo e a outra contém o produto máximo
- Estado de dependência: dp[i][0] e dp[i][1] são ambos dependentes: dp[i - 1][0] dp[i - 1][1] nums[i - 1]
- Os seguintes valores máximos e mínimos começam em: dp[i - 1][0] * nums[i - 1] , dp[i - 1][1] * nums[i - 1] e nums[i - 1 ] Escolha entre os três, nums[i - 1] significa desconectar o produto anterior e começar do zero
-
class Solution { //至于这个题目: 首先还是状态定义: //首先: 这个题目: 我们首先分析: 存在 一个负数 * 负变的非常大, 所以一定保存两个状态: //最小值状态 + 最大值状态: //dp[i][0] 和 dp[i][1] 定义: 前面的i个元素的子数组的最小最大乘积 public: int maxProduct(vector<int>& nums) { int n = nums.size(); int dp[n + 1][2]; dp[0][0] = 1, dp[0][1] = 1; //init int ans = INT_MIN; for (int i = 1; i <= n; ++i) { dp[i][0] = min(dp[i - 1][1] * nums[i - 1], dp[i - 1][0] * nums[i - 1]); dp[i][0] = min(dp[i][0], nums[i - 1]); //代表断开 dp[i][1] = max(dp[i - 1][1] * nums[i - 1], dp[i - 1][0] * nums[i - 1]); dp[i][1] = max(dp[i][1], nums[i - 1]); //代表断开 ans = max(dp[i][1], ans); //ans可能是在中间产生 } return ans; //最后返回这个最大值就是 } };
4. Resumo:
- Neste capítulo, distinguindo entre algoritmos recursivos e programação dinâmica, é apresentado que a essência da programação dinâmica é diferente dos algoritmos recursivos. a solução ótima.
- Então é entender o núcleo do algoritmo de programação dinâmica, definição de estado, esclarecer a definição de estado, sempre reconhecer o significado do estado e, em seguida, obter a equação de transição de estado (tomada de decisão) através do significado do assunto combinado com a definição de estado, e, em seguida, preste atenção ao init e ao limite.
- A definição do estado deve incluir todos os estados. O título implicará mais ou menos na definição do estado. Se a casa adjacente não puder ser roubada, há dois estados de roubo ou não roubo. Se as casas adjacentes forem tingidas de forma diferente, a cor do a casa é diferente.
- O próximo capítulo continua a otimização de algoritmos de programação dinâmica, principalmente otimização de espaço, técnicas de compressão de espaço + processamento de três problemas clássicos da mochila, além de questões de pincel