解题思路
首先用状态压缩,用一个int表示点(x,y),p = x * 131 + y;(131这里也可以用数据范围50)
用一个结构体virus记录将要感染的节点和墙的个数;
用一个哈希表记录病毒块的起点和其连通的病毒块;
探索每个病毒块时采用BFS,用set去重的存储将要感染的点,用wallnum记录墙的个数(注意墙的个数是上下左右0出现的次数,不等于0的个数),将访问到的病毒置为2防止重复访问;
找出所有病毒块中威胁最大的(即将要感染节点最多的),将这个病毒块封锁,即DFS这个连通的病毒块,置为999表示不可达,并累加墙的个数;
对于其他病毒块,感染他们将要感染的节点,即将要感染的节点从0置为1;
进行下一轮,直到没有0(全被感染),或没有病毒块,或病毒块已被封锁(所有病毒块的将要感染节点个数为0),退出循环。
代码
class Solution {
private:
vector<vector<int>> marx;
public:
int seed = 131;
int m, n;
vector<vector<int> > dir = {{0,1},{0,-1},{1,0},{-1,0}};
struct virus {
unordered_set<int> next; //将要感染节点
int wallnum;
};
void count_next(int x, int y, struct virus* v) {
queue<pair<int, int> > q;
q.push({x, y});
unordered_set<int> next;
int wallnum = 0;
marx[x][y] = 2;
while(!q.empty()) {
auto [a, b] = q.front();
//cout << a << "," << b << endl;
q.pop();
for(int i = 0; i < 4; i++) {
int nx = a + dir[i][0], ny = b + dir[i][1];
int p = nx * seed + ny;
if(nx < 0 || nx >= m || ny < 0 || ny >= n) continue; //越界
if(marx[nx][ny] == 999) continue; //阻挡
if(marx[nx][ny] == 0) {
++wallnum;
next.insert(p);
}
if(marx[nx][ny] == 1) {
q.push({nx, ny});
marx[nx][ny] = 2; //标记,避免找病毒块时再访问
}
}
}
//cout << wallnum << endl;
v->next = next; v->wallnum = wallnum;
}
void bulidwall(int x, int y) {
marx[x][y] = 999;
for(int i = 0; i < 4; i++) {
int nx = x + dir[i][0], ny = y + dir[i][1];
if(nx < 0 || nx >= m || ny < 0 || ny >= n) continue; //越界
if(marx[nx][ny] == 1) {
bulidwall(nx, ny);
}
}
return ;
}
int containVirus(vector<vector<int>>& isInfected) {
m = isInfected.size(); n = isInfected[0].size();
marx = isInfected;
int ans = 0;
while(1) {
unordered_map<int, virus> in_to_next;
//找到所有病毒块
bool iftherezero = false;
for(int i = 0; i < m; i++) {
for(int j = 0; j < n; j++) {
if(marx[i][j] == 1) {
struct virus v;
count_next(i, j, &v);
in_to_next[i*seed + j] = v;
}
if(marx[i][j] == 0) iftherezero = true; //有无幸存区
}
}
if(!iftherezero) break; //无幸存区,退出
if(in_to_next.size() == 0) break; //无病毒了
for(int i = 0; i < m; i++) {
for(int j = 0; j < n; j++) {
if(marx[i][j] == 2) marx[i][j] = 1; //将标记的病毒位复原
}
}
//找出威胁最大的病毒块
int mostthreaten = -1, mostpos = -1;
for(auto [nextpos, nextv] : in_to_next) {
int threaten = nextv.next.size();
if(threaten > mostthreaten) {
mostthreaten = threaten;
mostpos = nextpos;
}
}
if(mostpos == -1) break;
//设防火墙,将该病毒块置为999
ans += in_to_next[mostpos].wallnum;
bulidwall(mostpos / seed, mostpos % seed);
in_to_next.erase(mostpos);
//其他病毒感染,0变1
for(auto [nextpos, nextv] : in_to_next) {
for(auto p : nextv.next) {
marx[p/seed][p%seed] = 1;
}
}
}
return ans;
}
};