题目链接:www.marscode.cn/practice/w7…
题目中的seats表示所有人的戴口罩情况,若seats[r][c] == 1,则表示r行c列的那个人戴了口罩,若seats[r][c] == 0,则表示r行c列的那个人没戴口罩。patient[0]、patient[1]分别表示初始患者的行、列坐标。“邻座”的含义是前后左右的座位。
Dijkstra 算法 (贪心)
代码
时间复杂度: 。infectedTime数组的初始化的时间复杂度为。Dijkstra 算法的时间复杂度为。
空间复杂度: 。infectedTime数组的空间复杂度为。一个位置的感染时间虽然有可能被多次更新,但是更新仅发生于一个已经确定感染时间的邻居尝试感染周围人时。由于每个位置最多有 4 个邻居,因此总的更新次数上限是。
#include <iostream>
#include <vector>
#include <queue>
#include <limits>
#include <algorithm>
#include <array>
using namespace std;
static constexpr int NOT_INFECTED = numeric_limits<int>::max();
struct InfectionState {
int time;
int row;
int col;
bool operator>(const InfectionState &other) const {
return time > other.time;
}
};
int solution(int rowCount, int columnCount, vector<vector<int> > seats, vector<int> patient) {
const int patientRow = patient[0];
const int patientCol = patient[1];
auto inBound = [&rowCount, &columnCount](const int row, const int col)-> bool {
return row >= 0 and row < rowCount and col >= 0 and col < columnCount;
};
if (!inBound(patientRow, patientCol)) {
return 0;
}
vector<vector<int> > infectedTime(rowCount, vector<int>(columnCount, NOT_INFECTED));
infectedTime[patientRow][patientCol] = 0;
priority_queue<InfectionState, vector<InfectionState>, greater<> > minHeap;
minHeap.push({0, patientRow, patientCol});
static const array<pair<int, int>, 4> directions = {{{-1, 0}, {1, 0}, {0, -1}, {0, 1}}};
auto countInfectedNeighborsAtOrBefore = [&](const int r, const int c, const int t)-> int {
int infectedCount = 0;
for (const auto &[dr, dc]: directions) {
const int nr = r + dr;
const int nc = c + dc;
if (inBound(nr, nc) and infectedTime[nr][nc] <= t) {
++infectedCount;
}
}
return infectedCount;
};
int maxTime = 0;
while (!minHeap.empty()) {
InfectionState current = minHeap.top();
minHeap.pop();
if (current.time > infectedTime[current.row][current.col]) {
continue;
}
maxTime = max(maxTime, current.time);
for (const auto &[dr, dc]: directions) {
const int newRow = current.row + dr;
const int newCol = current.col + dc;
if (!inBound(newRow, newCol)) {
continue;
}
const int neighborMask = seats[newRow][newCol];
int candidateTime;
if (neighborMask == 0) {
candidateTime = current.time + 1;
} else {
const int infectedNeighborCount = countInfectedNeighborsAtOrBefore(newRow, newCol, current.time);
if (infectedNeighborCount >= 2) {
candidateTime = current.time + 1;
} else {
candidateTime = current.time + 2;
}
}
if (candidateTime < infectedTime[newRow][newCol]) {
infectedTime[newRow][newCol] = candidateTime;
minHeap.push({candidateTime, newRow, newCol});
}
}
}
return maxTime;
}
int main() {
vector<vector<int> > testSeats1 = {
{0, 1, 1, 1},
{1, 0, 1, 0},
{1, 1, 1, 1},
{0, 0, 0, 1}
};
vector<vector<int> > testSeats2 = {
{0, 1, 1, 1},
{1, 0, 1, 0},
{1, 1, 1, 1},
{0, 0, 0, 1}
};
vector<vector<int> > testSeats3 = {
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0}
};
vector<vector<int> > testSeats4 = {
{1, 1, 1, 1},
{1, 1, 1, 1},
{1, 1, 1, 1},
{1, 1, 1, 1}
};
vector<vector<int> > testSeats5 = {
{1}
};
cout << (solution(4, 4, testSeats1, {2, 2}) == 6) << endl;
cout << (solution(4, 4, testSeats2, {2, 5}) == 0) << endl;
cout << (solution(4, 4, testSeats3, {2, 2}) == 4) << endl;
cout << (solution(4, 4, testSeats4, {2, 2}) == 6) << endl;
cout << (solution(1, 1, testSeats5, {0, 0}) == 0) << endl;
return 0;
}
思路
-
维护一个 数组。其中 表示坐在 位置的人最早在第几秒被感染。初始时,除患者外都设为“未感染”(用来表示),初始患者本人的最早感染时间设为0。
-
使用最小堆按时间升序处理感染
- 堆中存储三元组 ,表示位置 最早在时间 被感染。
- 每次从堆顶取出当前时间最小的条目 。若 大于 ,说明已经有更早的方式感染了该位置,则跳过。
- 否则,枚举它的四个邻座 ,计算该邻座基于当前时刻 的可能感染时间。若该时间早于 ,则更新并推入堆。
-
传染规则的实现
-
若邻座未戴口罩(),只需 1 秒即可感染。
-
若邻座戴口罩(),则需要判断在时刻 之前或正好 时刻有多少个邻座已被感染:
- 若已感染邻居数 ,则只需 1 秒即可感染。
- 否则,需要 2 秒感染。
-
-
在所有可感染的位置都更新完毕后, 数组中最大的那个值就是感染全场所用的最短时间。