前書き: 最近、筆記試験と面接の準備をしていたときに、動的プログラミングをテストするのが大好きな知識ポイントを見つけたので、ここで整理します。
1 最小パスと
Lituo 64 問題
解決策
タイトル: 非負の整数を含む mxn グリッド グリッドが与えられた場合、パス上の数値の合計が最小になるように、左上隅から右下隅までのパスを見つけてください。
注: 一度に 1 ステップずつ下または右に移動してください。
コード:
def minPathSum(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
if not grid or not grid[0]:
return 0
row, col = len(grid), len(grid[0])
dp = [[0] * col for _ in range(row)]
dp[0][0] = grid[0][0]
for i in range(1, row):
dp[i][0] = dp[i - 1][0] + grid[i][0]
for j in range(1, col):
dp[0][j] = dp[0][j - 1] + grid[0][j]
for i in range(1, row):
for j in range(1, col):
dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j]
return dp[row - 1][col -1]
2 編集距離
質問: word1 と word2 という 2 つの単語が与えられた場合、word1 を word2 に変換するために使用される演算の最小数を計算してください。
単語に対して次の 3 つの操作を実行できます: 文字の挿入、文字の削除、文字
コードの置換:
def minDistance(self, word1, word2):
n = len(word1)
m = len(word2)
# 有一个字符串为空串
if n * m == 0:
return n + m
# DP 数组
D = [ [0] * (m + 1) for _ in range(n + 1)]
# 边界状态初始化
for i in range(n + 1):
D[i][0] = i
for j in range(m + 1):
D[0][j] = j
# 计算所有 DP 值
for i in range(1, n + 1):
for j in range(1, m + 1):
left = D[i - 1][j] + 1
down = D[i][j - 1] + 1
left_down = D[i - 1][j - 1]
if word1[i - 1] != word2[j - 1]:
left_down += 1
D[i][j] = min(left, down, left_down)
return D[n][m]
3 つの最長回文サブシーケンス
Likou 516 question
solution
title : 文字列 s を与え、その中で最も長い回文部分列を見つけ、列の長さを返します。サブシーケンスは、一部の文字を削除するか、残りの文字の順序を変更せずに文字を削除しないことによって形成されるシーケンスとして定義されます。
コード:
def longestPalindromeSubseq(self, s):
n = len(s)
dp = [[0] * n for _ in range(n)]
for i in range(n - 1, -1, -1):
dp[i][i] = 1
for j in range(i + 1, n):
if s[i] == s[j]:
dp[i][j] = dp[i + 1][j - 1] + 2
else:
dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])
return dp[0][n - 1]
4 2 つの文字列の削除操作
Likou 583 question
solution
title : 2 つの単語 word1 と word2 が与えられた場合、word1 と word2 を同じにするために必要な最小ステップ数を見つけます。各ステップで任意の文字列の文字を削除できます。
コード:
def minDistance(self, word1, word2):
"""
:type word1: str
:type word2: str
:rtype: int
"""
m, n = len(word1), len(word2)
dp = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(1, m + 1):
for j in range(1, n + 1):
if word1[i-1] == word2[j-1]:
dp[i][j] = dp[i - 1][j - 1] + 1
else:
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
max_sub = dp[m][n]
return m - max_sub + n - max_sub
5 つの最長共通部分列
Likou 1143 question
solution
title : 2 つの文字列 text1 と text2 が与えられた場合、これら 2 つの文字列の最長共通部分列の長さを返します。共通のサブシーケンスが存在しない場合は 0 を返します。
コード:
def longestCommonSubsequence(self, text1, text2):
"""
:type text1: str
:type text2: str
:rtype: int
"""
m, n = len(text1), len(text2)
dp = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(1, m+1):
for j in range(1, n+1):
if text1[i-1] == text2[j-1]:
dp[i][j] = dp[i-1][j-1] + 1
else:
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
return dp[m][n]
6 文字列を回文にするための最小挿入数
Lituo 1312 question
solution
title : 文字列 s が与えられ、操作ごとに文字列の任意の位置に任意の文字を挿入できます。
s が回文になる最小の操作数を返してください。
コード:
def minInsertions(self, s):
n = len(s)
t = s[::-1]
dp = [[0] * (n + 1) for _ in range(n + 1)]
for i in range(1, n + 1):
for j in range(1, n + 1):
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
if s[i - 1] == t[j - 1]:
dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + 1)
return n - dp[n][n]
7 最低乗車料金
トピック: ある通りには 1 km ごとにバス停があり、バス料金は次のとおりです。
キロメートル | 料金 |
---|---|
1 | 12 |
2 | 21 |
3 | 31 |
4 | 40 |
5 | 49 |
6 | 58 |
7 | 69 |
8 | 79 |
9 | 90 |
10 | 101 |
また、車は 10 キロ以上移動することはありません。誰かが何回でも車を乗り換えることができると仮定して、n キロ移動したいと考えています。費用を最小限に抑えるための旅行計画を見つけるのを手伝ってください (10 キロの費用が 1 キロ未満であれば許可されます)。
入力: 入力ファイルには 2 つの行があり、最初の行は 100 を超えない 10 個の整数で、1 ~ 10 キロメートルを順番に運転するコストを表し、隣接する 2 つの数値はスペースで区切られています。誰かが運転したいキロメートル。
出力: 出力ファイルには、このテスト ポイントの最小コストを表す整数を含む 1 行のみが含まれます。
サンプル入力:
12 21 31 40 49 58 69 79 90 101
15
サンプル出力: 147
コード:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int f[21],dp[111];
int n;
int main()
{
memset(dp,127,sizeof(dp));
for(int i=1;i<=10;i++)
scanf("%d",&f[i]);
scanf("%d",&n);
dp[0]=0;
dp[1]=f[1];
for (int i=2;i<=n;i++)
{
dp[i]=dp[i-1]+f[1];
for (int j=2;j<=10 && j<=i;j++)
dp[i]=min(dp[i],dp[i-j]+f[j]);
}
printf("%d",dp[n]);
return 0;
}