思路:这道题目是让我们求出在经过旷日持久的互殴后至少能够留下多少只怪兽。本质是要我们根据怪兽间关系,构造有向图寻找强连通分量。如果有两个强连通分量那么每个强连通分量中至少会有一只怪兽留下,假设一个有向图中强连通分量个数为n,如果强连通分量间互不连通,那么至少能够留下n只怪兽;若两个强连通分量之间存在一条边相连,那个怪兽的数量为n-1。这道题使用Tarjan来寻找强连通分量,代码实现如下:
class Solution {
private:
stack<int> s;
vector<int> sccno;//标示每个点所属连通分量
vector<vector<int> > graph;
vector<int> pre, lowlink;
vector<bool> visit;
int scc_cnt;
int cnt;
void Tarjan(int v) {
pre[v] = lowlink[v] = ++cnt;
s.push(v);
for (int j = 0; j < graph[v].size(); j++) {
int u = graph[v][j];
if (!pre[u]) {
Tarjan(u);
lowlink[v] = min(lowlink[v], lowlink[u]);
} else if (!sccno[u]) {
lowlink[v] = min(lowlink[v], pre[u]);
}
}
if (pre[v] == lowlink[v]) {
scc_cnt++;
do {
int idx = s.top(); s.pop();
sccno[idx] = scc_cnt;
} while (idx != v);
}
}
void find_scc(int n) {
//init
sccno.resize(n, 0);
pre.resize(n, 0);
lowlink.resize(n, 0);
scc_cnt = 0;
cnt = 0;
for (int i = 0; i < n; i++) {
if (!pre[i]) Tarjan(i);
}
}
public:
int minLeftMonsters(vector<vector<char> > G) {
int n = G.size();
graph.resize(n);
for (int i = 0; i < n; i++) {
for (int j = i+1; j < n; j++) {
if (G[i][j] == '+') graph[j].push_back(i);
else if (G[i][j] == '-') graph[i].push_back(j);
}
}
find_scc(n);
int res = scc_cnt;
visit.resize(res+1, false);
for (int i = 0; i < n; i++) {
for (int j = 0; j < graph[i].size(); j++) {
int x = sccno[graph[i][j]];
if (x == sccno[i]) continue;
if (!visit[x]) {//连通分量之间存在相连边
res--;
visit[x] = true;
}
}
}
sccno.clear();
pre.clear();
lowlink.clear();
visit.clear();
for (int i = 0; i < n; i++) graph[i].clear();
while (!s.empty()) s.pop();
return res;
}
};