目次
2.2配列が与えられると、反復的な数、反復的ではない順列があります
3.2配列が与えられた場合、繰り返しの数はなく、再利用可能な組み合わせ
3.3配列が与えられると、繰り返される数と繰り返されない組み合わせがあります(難しい)
0記事の紹介
この記事では、Python言語を使用して、アルゴリズムの深さ優先探索の順列と組み合わせの問題と、リートコードのトピックを紹介します。
順列と組み合わせを3つの状況に分けます
- 繰り返しなし、繰り返しなし
- 繰り返しなし、再利用可能
- 繰り返しではなく、繰り返しの数があります
順列と組み合わせに応じて、4つのタイプがあり、繰り返し使用できる繰り返し番号があります。この場合、最初に繰り返し番号を削除して、3番目のケースに切り替えます。
1dfsルーチン
对于每一种情况x:
dfs(x)
def dfs(x):
if 情况x不满足当前条件:
return
if x是最后一个元素了:
搜到了,做操作
return
标记x情况搜过了
for y in 由x衍生出的其他条件:
dfs(y)
标记x情况没搜过
2順列
2.1配列が与えられた場合、反復数はなく、反復しない順列
配列の順序を逆にすることができます
質問:繰り返される数のない配列が与えられた場合、すべての順列を返します
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
def dfs(i):
if i==n: ans.append(nums.copy())
for x in range(i,n):
nums[i],nums[x]=nums[x],nums[i]
dfs(i+1)
nums[i],nums[x]=nums[x],nums[i]
n=len(nums)
ans=[]
dfs(0)
return ans
2.2配列が与えられると、反復的な数、反復的ではない順列があります
47.完全な配置II-LeetCode(leetcode-cn.com)
質問:繰り返される数の配列が与えられた場合、すべての順列を返します
現在の位置の値が以前に表示されていないことを確認してください。要素をdictに入れ、キーは要素、値は出現回数です。毎回dictをトラバースするだけで十分です。
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
def dfs(i):
if i==n: ans.append(nums.copy())
for x in range(i,n):
nums[i],nums[x]=nums[x],nums[i]
dfs(i+1)
nums[i],nums[x]=nums[x],nums[i]
n=len(nums)
ans=[]
dfs(0)
return ans
2.3配列が与えられ、繰り返し数がなく、再利用可能な順列
これは完全なナップサック問題であり、部分的な問題です
質問:正の整数で構成され、繰り返しの数がない配列が与えられた場合、合計が与えられたターゲットの正の整数である組み合わせの数を見つけます。
class Solution:
def combinationSum4(self, nums: List[int], target: int) -> int:
dp=[0]*(target+1)
dp[0]=1
for i in range(target+1):
for num in nums:
if i>=num:
dp[i]+=dp[i-num]
return dp[target]
3つの組み合わせ
組み合わせの相対的な配置ははるかに単純であり、順序を考慮する必要はありません。この要素があるかどうかを考慮するだけです。
3.1配列が与えられ、反復数がなく、反復しない組み合わせ
質問の意味:合計nになるk個の数のすべての組み合わせを見つけます。組み合わせには1から9までの正の整数のみが許可され、各組み合わせで繰り返される数値はありません。
注:すべての数値は正の整数です。ソリューションセットに繰り返しの組み合わせを含めることはできません。
回答:通常の深さ優先探索で問題ありません
class Solution:
def combinationSum3(self, k: int, n: int) -> List[List[int]]:
def dfs(num,tmpsum):
if len(tmp)==k:
if tmpsum==n:ans.append(tmp.copy())
return
elif tmpsum>n:return
for i in range(num,10):
tmp.append(i)
dfs(i+1,tmpsum+i)
tmp.pop()
ans,tmp=[],[]
dfs(1,0)
return ans
3.2配列が与えられた場合、繰り返しの数はなく、再利用可能な組み合わせ
質問の意味:繰り返し要素のない配列候補とターゲット番号のターゲットが与えられた場合、番号とターゲット番号を作成できる候補のすべての組み合わせを見つけます。候補者の数は、制限なく繰り返し選択できます。
回答:要素を繰り返し選択できるため、次の深さ優先探索方法を少し変更できます
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
def dfs(idx,total):
if total==target:ans.append(tmp.copy())
elif total>target:return
for i in range(idx,n):
tmp.append(candidates[i])
dfs(i,total+candidates[i])
tmp.pop()
candidates.sort()
n=len(candidates)
ans,tmp=[],[]
dfs(0,0)
return ans
3.3配列が与えられると、繰り返される数と繰り返されない組み合わせがあります(難しい)
候補の配列とターゲット数のターゲットが与えられた場合、数の合計をターゲットにすることができる候補のすべての組み合わせを見つけます。
候補者の各番号は、各組み合わせで1回のみ使用できます。
分析:この質問の難しさは、1,1,1,3、target = 5がある場合です。5を構成する2つの1の3つの組み合わせがありますが、それらは同じであり、組み合わせる必要があります。この質問の操作は、繰り返される要素は最初のパスでのみスイープできるということです。つまり、構成要素に1があり、この1は最初の1の位置から取得され、通常どおり逆方向に検索されます。構成要素に1があり、この1は最初の1の位置からのものではないため、検索を中止して、1ではない最初の番号の位置を検索します。
class Solution:
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
def dfs(idx,total):
if total>target:return
elif total==target:
ans.append(tmp.copy())
return
for i in range(idx,n):#重复的只允许第一遍扫过去
if i>idx and candidates[i]==candidates[i-1]:continue
tmp.append(candidates[i])
dfs(i+1,total+candidates[i])
tmp.pop()
candidates.sort()
n=len(candidates)
tmp,ans=[],[]
dfs(0,0)
return ans