力扣--二進位制列舉
題目:
我們有 n 棟樓,編號從 0 到 n - 1 。每棟樓有若干員工。由於現在是換樓的季節,部分員工想要換一棟樓居住。
給你一個數組 requests ,其中 requests[i] = [fromi, toi] ,表示一個員工請求從編號為 fromi 的樓搬到編號為 toi 的樓。
一開始 所有樓都是滿的,所以從請求列表中選出的若干個請求是可行的需要滿足 每棟樓員工淨變化為 0 。意思是每棟樓 離開 的員工數目 等於 該樓 搬入 的員工數數目。比方說 n = 3 且兩個員工要離開樓 0 ,一個員工要離開樓 1 ,一個員工要離開樓 2 ,如果該請求列表可行,應該要有兩個員工搬入樓 0 ,一個員工搬入樓 1 ,一個員工搬入樓 2 。
請你從原請求列表中選出若干個請求,使得它們是一個可行的請求列表,並返回所有可行列表中最大請求數目。
解答:
我們可以通過回溯的方式列舉每一個請求是否被選擇。
定義函式 \text{dfs}(\textit{pos})dfs(pos) 表示我們正在列舉第 \textit{pos}pos 個請求。同時,我們使用陣列 \textit{delta}delta 記錄每一棟樓的員工變化量,以及變數 \textit{cnt}cnt 記錄被選擇的請求數量。
對於第 \textit{pos}pos 個請求 [x,y][x,y],如果選擇該請求,那麼就需要將 \textit{delta}[x]delta[x] 的值減 11,\textit{delta}[y]delta[y] 的值加 11,\textit{cnt}cnt 的值加 11;如果不選擇該請求,則不需要進行任何操作。在這之後,我們呼叫 \text{dfs}(\textit{pos}+1)dfs(pos+1) 列舉下一個請求。
如果我們列舉完了全部請求,則需要判斷是否滿足要求,也就是判斷 \textit{delta}delta 中的所有值是否均為 00。若滿足要求,則更新答案的最大值。
程式碼實現時,可以在修改 \textit{delta}delta 的同時維護 \textit{delta}delta 中的 00 的個數,記作 \textit{zero}zero,初始值為 nn。如果 \textit{delta}[x]delta[x] 增加或減少前為 00,則將 \textit{zero}zero 減 11;如果 \textit{delta}[x]delta[x] 增加或減少後為 00,則將 \textit{zero}zero 加 11。
Python3C++JavaC#GolangCJavaScript
class Solution {
private:
vector<int> delta;
int ans = 0, cnt = 0, zero, n;
public:
void dfs(vector<vector<int>> &requests, int pos) {
if (pos == requests.size()) {
if (zero == n) {
ans = max(ans, cnt);
}
return;
}
// 不選 requests[pos]
dfs(requests, pos + 1);
// 選 requests[pos]
int z = zero;
++cnt;
auto &r = requests[pos];
int x = r[0], y = r[1];
zero -= delta[x] == 0;
--delta[x];
zero += delta[x] == 0;
zero -= delta[y] == 0;
++delta[y];
zero += delta[y] == 0;
dfs(requests, pos + 1);
--delta[y];
++delta[x];
--cnt;
zero = z;
}
int maximumRequests(int n, vector<vector<int>> &requests) {
delta.resize(n);
zero = n;
this->n = n;
dfs(requests, 0);
return ans;
}
};
複雜度分析
時間複雜度:O(2^m)O(2
m
),其中 mm 是陣列 \textit{requests}requests 的長度,即請求的數量。從 mm 個請求中任意選擇請求的方案數為 2^m2
m
,對於每一種方案,我們需要 O(1)O(1) 的時間判斷其是否滿足要求。
空間複雜度:O(m+n)O(m+n)。遞迴需要 O(m)O(m) 的棧空間,陣列 \textit{delta}delta 需要 O(n)O(n) 的空間。
方法二:二進位制列舉
我們可以使用一個長度為 mm 的二進位制數 \textit{mask}mask 表示所有的請求,其中 \textit{mask}mask 從低到高的第 ii 位為 11 表示選擇第 ii 個請求,為 00 表示不選第 ii 個請求。我們可以列舉 [0,2^m-1][0,2
m
−1] 範圍內的所有 \textit{mask}mask,對於每個 \textit{mask}mask,依次列舉其每一位,判斷是否為 11,並使用與方法一相同的陣列 \textit{delta}delta 以及變數 \textit{cnt}cnt 進行統計,在滿足要求時更新答案。
Python3C++JavaC#GolangCJavaScript
class Solution:
def maximumRequests(self, n: int, requests: List[List[int]]) -> int:
ans = 0
for mask in range(1 << len(requests)):
cnt = mask.bit_count()
if cnt <= ans:
continue
delta = [0] * n
for i, (x, y) in enumerate(requests):
if mask & (1 << i):
delta[x] += 1
delta[y] -= 1
if all(x == 0 for x in delta):
ans = cnt
return ans
複雜度分析
時間複雜度:O(2^m \times n)O(2
m
×n),其中 mm 是陣列 \textit{requests}requests 的長度,即請求的數量。從 mm 個請求中任意選擇請求的方案數為 2^m2
m
,對於每一種方案,我們需要 O(n)O(n) 的時間判斷其是否滿足要求。
空間複雜度:O(n)O(n)。陣列 \textit{delta}delta 需要 O(n)O(n) 的空間。