問題の説明:
アレイは、両方のサブセット内の要素の総和が等しくなるように2つのサブセットに分割することができる場合にのみ正の整数を含む非空の配列を指定し、見つけます。
注意:
- アレイ素子の各々は、100を超えません。
- アレイのサイズは、200を超えません。
例1:
Input: [1, 5, 11, 5]
Output: true
Explanation: The array can be partitioned as [1, 5, 5] and [11].
例2:
Input: [1, 2, 3, 5]
Output: false
Explanation: The array cannot be partitioned into equal sum subsets.
問題解決のアイデア:
同じサブセットに、次のプロパティを取得するのは簡単:
- 一つだけの数ならば、それは確かに分割することはできません。
- すべての数字とが奇数の場合、確かに分割することはできません。
- 目標は、2で割った全ての合計を分割することです。
方法1:使用貪欲アルゴリズム:
- すべての最初の数であった降順。
- ダブルポインタI、J、I jを後退させる現在の数を指します。
- 二重ループは、外側ループ番号i各時点、Jの移動中に内部ループ:
1、および電流積算<ターゲット、および更新蓄積する場合、
2、真蓄積==ターゲットが、返された場合。
3、累積和>ターゲット場合は、何もしません。 - 最後に、あなたは、分割することができない場合はfalseを返します。
例えば:NUMSは= [10,8,7,6,5,4]、初めてループするため、私は後者の図形トラバーサルJの間、8と10がにロードすることができ、10点ではなく、第二ループは、iは、トラバース中jは、8,7および5にロードされ、そして20と正確に一致することができる8を指し、真を返します。
注:ので、降順でソートされている必要があり、我々は貪欲な戦略を使用します。すべての最初の大きな大、詰め替え回インストール。ターゲットよりも大きな負荷時間は、それが戻って第三、第四...大型ピックアップしていきますので、それは実現可能である場合。ソートがNUMSようでない場合は、= [4,5,6,7,8,10]、上記の方法をとることはFalseを返します。
時間計算量はO(N ^ 2)であり、空間的な複雑さはO(1)です。
以下を達成するためのpython3:
class Solution:
def canPartition(self, nums):
lens = len(nums)
if lens == 1:
return False
tot = sum(nums)
if tot % 2 == 1:
return False
target = tot // 2
nums.sort(reverse=True) # 从大到小排序
for i in range(lens):
tem = 0
for j in range(i, lens):
if tem + nums[j] == target:
return True
elif tem + nums[j] < target:
tem += nums[j]
return False
print(Solution().canPartition([6,5,4,8,10,7])) # True
方法2:使用ダイナミックソルバー:
:実際には、この質問は01-ナップザック問題のアプリケーションである01-ナップザック問題とその応用。
配列のサブ要素が存在する場合、それの元の配列要素の半分に正確に等しい:我々は次の形式に変換することができますか?内部かサブアレイのサブアレイに(この最初の部分配列の存在を仮定):元の配列要素のそれぞれについて二つの状態を有します。このようなビューは、我々は問題とナップザック問題は非常に似ているので、我々は、道路上の問題を解決0-1ナップザック問題へのソリューションを使用することができます見つけることができるようになります。
問題が存在する中で、物品、値の要素である商品の重量値とみなすことができ、元の配列の各要素と、元の配列の半分がバックパックの最大軸受け重量と考えることができます、ときのバックパックは、元の配列の商品の半分の最大値を下に置くことができますし、それはそうでない場合はfalse、trueを返します。
したがって、我々は簡単に次のアルゴリズムを取得することができます:
- この問題は、ナップサック、アレイの半分の容量で、それが必要である
dp[len(nums)+1][sum(nums)//2+1]
配列の大きさdp[i][j]
I jのバックパックの容量目標値の前に配置数を表します。 - jはi番目の電流容量、すなわちJ <NUMS [I-1]よりも小さい場合 、 次いで
dp[i][j] = dp[i-1][j]
、 - それ以外の場合は、
dp[i][j] = max(dp[i-1][j], dp[i-1][j-nums[i-1]] + nums[i-1])
; - テーブルの各行に記入するときは、目標値に到達したかを判断すべきである。つまり、
dp[i][j]
かなりのテーブルに更新のためにすべての行を待っているよりも、それは本当であるかどうか、その後の判断を終えました。 - あなたはすべての行、更新された場合は
dp[-1][-1]
、目標値と等しくないし、それはFalseを返し、分割することはできません。
時間計算量はO(N ^ 2)で、空間的な複雑さはO(N *(合計(NUMS)// 2))です。
達成のpython3:
class Solution:
def canPartition(self, nums: List[int]) -> bool:
lens = len(nums)
if lens == 1:
return False
tot = sum(nums)
if tot % 2 == 1:
return False
tar = tot // 2
dp = [[0] * (tar+1) for _ in range(lens+1)]
for i in range(1, lens + 1):
for j in range(1, tar + 1):
if j < nums[i-1]:
dp[i][j] = dp[i-1][j]
else:
dp[i][j] = max(dp[i-1][j], dp[i-1][j-nums[i-1]] + nums[i-1])
if dp[i][j] == tar: # 更新完一行就应该判断一下
return True
return False
print(Solution().canPartition([2,5,3,4])) # True
print(Solution().canPartition([5,1,3,5])) # False
注:この種の問題、すなわち項目(アレイ要素)被験体の存在下または非存在下で二つの状態があるが、それは考えて、溶液01-バックパックによって解決することができます。
ます。https://www.jianshu.com/p/5f0a8a72a50fで再現