今天要寫的是狀態壓縮
DP在記錄狀態的時候有許多不同的方式,如果要記錄的狀態太多,或需要使用的維度太高,就會考慮使用狀態壓縮。
直接用例題來解釋:
訪問所有節點的最短路徑
題目敘述:給定一張無向圖,返回任意選擇一起點,訪問完所有節點的最短路徑長(可用相同的邊)((也就是可以走回重複的點))
思路:
題目本身的問題並不複雜,就是選一個起點,從起點找完所有的節點,紀錄最短的路徑長,返回答案就好,思路本身也很簡單,就是暴力法,把每一個種組合都找一次,問題只是實現,既然路徑可以返回,就不能單純只是記錄每一個節點的訪問或未被訪問,這時候就需要狀態壓縮。
題目中,節點數量N小於12,所以可以用這種方式紀錄:
vis{start}{mask}
start代表起始節點,mask代表訪問的狀態
要只用一個維度表示N個節點的訪問狀態,就是狀態壓縮的精髓,其實就是用位元來記錄
ex. 00010可以代表從1號節點出發,還沒訪問過其他節點
00011可以代表1,2號節點都被訪問過了
有了這種紀錄方式之後,就對每一個起點BFS就好,只要遇到有0111⋯⋯11這種狀態(全部訪問完成)就返回答案
class Solution {
public:
int shortestPathLength(vector<vector<int>>& graph) {
int n = graph.size();
queue< tuple<int, int, int> > q;
vector<vector<bool>> vis(n, vector<bool>(1 << n));
for(int i = 0; i < n; i++) {
q.push({i, 1 << i, 0});
vis[i][1 << i] = true;
}
while(!q.empty()) {
auto [cur, mask, dist] = q.front();
q.pop();
if(mask == (1 << n) - 1) return dist;
for(int x : graph[cur]) {
int nextmask = mask | (1 << x);
if(!vis[x][nextmask]) {
q.push({x, nextmask, dist + 1});
vis[x][nextmask] = true;
}
}
}
return 0;
}
};