1. 程式人生 > 實用技巧 >一和零(動態規劃+多維揹包問題)

一和零(動態規劃+多維揹包問題)

題目


給你一個二進位制字串陣列 strs 和兩個整數 m 和 n 。

請你找出並返回 strs 的最大子集的大小,該子集中 最多 有 m 個 0 和 n 個 1 。

如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。

示例 1:

輸入:strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3
輸出:4
解釋:最多有 5 個 0 和 3 個 1 的最大子集是 {"10","0001","1","0"} ,因此答案是 4 。
其他滿足題意但較小的子集包括 {"0001","1"} 和 {"10","1","0"} 。{"111001"} 不滿足題意,因為它
含 4 個 1 ,大於 n 的值 3 。
示例 2:

輸入:strs = ["10", "0", "1"], m = 1, n = 1
輸出:2
解釋:最大的子集是 {"0", "1"} ,所以答案是 2 。

提示:

1 <= strs.length <= 600
1 <= strs[i].length <= 100
strs[i]僅由'0' 和'1' 組成
1 <= m, n <= 100

原題跳轉連結

解題思路


第一次碰到這樣的題,今天一口氣刷了兩道,兩道都是動態規劃問題,並且本題是多維的揹包0/1問題。多維揹包問題和一維揹包問題很像,但是有時可以進行空間上的優化。不太明白揹包問題的可以看下這個視訊bilili

對於揹包問題,最重要的就是得到揹包的狀態轉移方程,對於本題狀態方程應該為:
現有 X 種物品,每種物品體積為 Wx ,價值為 Vx

ans[x][j + wx][k + wx] = max(ans[x - 1][i + wx][k + wx], ans[x - 1][i][k] + vx)

當揹包放得下時用上面的狀態方程進行揹包狀態的更新

    /*放得下但不放*/
    ans[x - 1][i + wx][k + wx]
    /*移除體積後,放入*/
    ans[x - 1][i][k] + vx
    ```

對於本題,可以維護一個三維陣列來記錄狀態。

int ans[strsSize + 1][m + 1][n + 1];

第一維表示新增第i個字串,m,n分別表示0,1的剩餘空間。
三重迴圈更新揹包狀態。

for(int j = 0; j <= m; ++j){
for(int k = 0; k <= n; ++k){
if(j >= mc && k >= nc){//放得下,看放不放
ans[i][j][k] = max(ans[i - 1][j][k], ans[i - 1][j - mc][k - nc] + 1);
}else{//放不下
ans[i][j][k] = ans[i - 1][j][k];
}
}
}


完整程式碼如下:

void get_count(char *str, int *mc, int *nc){
int m = 0, n = 0;
for(int i = 0; str[i]; i++){
if(str[i] == '0'){
m++;
}
if(str[i] == '1'){
n++;
}
}
*mc = m;
*nc = n;
}

int max(int a, int b){
return a > b ? a : b;
}

int findMaxForm(char ** strs, int strsSize, int m, int n){
int ans[strsSize + 1][m + 1][n + 1];
memset(ans, 0, sizeof(ans));
for(int i = 1; i <= strsSize; i++){
int mc, nc;
get_count(strs[i - 1], &mc, &nc);
for(int j = 0; j <= m; ++j){
for(int k = 0; k <= n; ++k){
if(j >= mc && k >= nc){//放得下,看放不放
ans[i][j][k] = max(ans[i - 1][j][k], ans[i - 1][j - mc][k - nc] + 1);
}else{
ans[i][j][k] = ans[i - 1][j][k];
}
}
}
}
return ans[strsSize][m][n];
}