分割等和子集 遞迴 二維dp 一維dp

2021-10-07 05:05:00 字數 1636 閱讀 4256

問題描述:

給定乙個只包含正整數的非空陣列。是否可以將這個陣列分割成兩個子集,使得兩個子集的元素和相等。

大體思路:

由於想將陣列分割為兩個和相同的子集,因此可以先獲得陣列整體的和,然後該問題可以轉化為找陣列中是否存在某幾個元素之和等於總和的一半。注:若總和為奇數則可以直接返回false,由於陣列全為正整數,不會出現0.5的。

遞迴解法:

若給定乙個函式dfs(i,  target)表示從i位置開始能否組成target的值,對於每個i有選和不選兩種可能,想要返回true(構成target),要麼dfs(i + 1, target)返回true,要麼dfs(i + 1, target - nums[i])為true。實現**入下:

public boolean dfs(int index, int nums, int target)

return dfs(index + 1, nums, target) || dfs(index + 1, nums, target - nums[index]);

}

二維dp:

由於上述遞迴解法中存在大量的重複計算,而返回值只與index和target的取值有關,因此很容易想到改為二維dp.該解法中dp[i][j] 表示從i位置開始能否構成j。

初值:dp[length][0] = true dp[length][j] = false j != 0

轉移函式:dp[i][j] = dp[i + 1][j] || dp[i + 1][j - nums[i]] 

實現**如下:

public boolean canpartition(int nums) 

if(sum % 2 != 0)

// 二維dp dp[i][j] 表示從i位置開始可以構成j

boolean dp = new boolean[nums.length + 1][sum / 2 + 1];

dp[nums.length][0] = true;

for(int i = nums.length - 1; i >= 0; i--)}}

return dp[0][sum / 2];

}

一維dp:

我們在二維dp中發現dp[i][ : ] 只與dp[i + 1][ : ]有關,因此很容易想到讓他們共用乙個一維陣列,即dp[j] 為從當前位置開始能否構成j。此外我們發現dp[i][j] 的取值只有其正下方 和左下方的值有關,因此應該從右往左依次填表。(若從左至右填,會出現把第i1代當第i+1代)。

初值:dp[0] = true dp[j] = false j != 0

轉移函式:dp[j] = dp[j] || dp[j - nums[i]] 

實現**如下:

public boolean canpartition(int nums) 

if(sum % 2 != 0)

// 空間壓縮 一維dp[i]

boolean dp = new boolean[sum / 2 + 1];

dp[0] = true;

for(int i = nums.length - 1; i >= 0; i--)

}return dp[sum / 2];

}

棋盤分割(二維區間DP)

題目大意 給乙個棋盤,棋盤上每個格仔中都有乙個值,現在需要將棋盤切成n個矩形,總共切n 1刀,求最小的均方差。均方差定義為 其中。題目分析 將均方差化簡得到 均方差2 xi 2 n 平均值2。顯然,平均值2是定值,為數字總和除以n。只需讓矩形的和的平方和最小即可。先預處理出陣列s x1,y1,x2,...

字首和(一維 二維)

1.一維字首和 字首和 s i a 1 a 2 a i 區間和 l,r a l a r s r s l 1 題目795 輸入乙個長度為n的整數序列。接下來再輸入m個詢問,每個詢問輸入一對l,r。對於每個詢問,輸出原序列中從第l個數到第r個數的和。輸入格式 第一行包含兩個整數n和m。第二行包含n個整數...

一維 二維字首和

基礎知識 一維字首和 s i a 1 a 2 a i a l a r s r s l 1 二維字首和 s i,j 第 i 行 j 列格仔左上部分所有元素的和 以 x1,y1 為左上角,x2,y2 為右下角的子矩陣的和為 s x2,y2 s x1 1,y2 s x2,y1 1 s x1 1,y1 1 ...