DAG的覆盖与转换SCC问题
给定一个有向无环图 G G G,请问:
- 选择最少的节点数 A A A,可以遍历整个图 G G G。
- 添加最少的边 B B B,使得整个图变成一个强连图分量。
问题1
考虑拓扑排序,根据贪心的思想,我们从入度为 0 0 0的节点开始选择,因此答案就是入度为 0 0 0的点的个数。
问题2
考虑贪心,每次我们都选择一个最大的路径,将其首尾连接,变成一个环,使得一个入度为 0 0 0(起点),出度为 0 0 0(终点),加入到强连通分量中。直到变成一个强连通分量(出入度为 0 0 0的点只有一个)为止。因此,答案就是出度为 0 0 0的点的数量和入度为 0 0 0的点的数量中的最大值。
例题
Tarjan缩点+应用结论
#include <bits/stdc++.h>
using namespace std;
#define FR freopen("in.txt", "r", stdin)
#define FW freopen("out.txt", "w", stdout)
#define MAXT INT_MAX
typedef long long ll;
struct Edge
{
int from;
int to;
int nxt;
} e[10005];
int head[105];
int tot = 0;
inline void add(int u, int v)
{
tot++;
e[tot].to = v;
e[tot].from = u;
e[tot].nxt = head[u];
head[u] = tot;
}
int n;
int low[105];
int dfn[105];
int inStack[105];
int SCCID[105];
stack<int> sta;
int ti = 0;
int idx = 0;
int graph[105][105];
int inDeg[105];
int outDeg[105];
void tarjan(int u)
{
ti++;
dfn[u] = low[u] = ti;
inStack[u] = true;
sta.push(u);
for (int ne = head[u]; ne != 0; ne = e[ne].nxt)
{
int v = e[ne].to;
if (dfn[v] == 0)
{
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if (inStack[v])
{
low[u] = min(low[u], dfn[v]);
}
}
if (low[u] == dfn[u])
{
idx++;
int curr;
do
{
curr = sta.top();
sta.pop();
inStack[curr] = false;
SCCID[curr] = idx;
} while (curr != u);
}
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
int v;
while (1)
{
scanf("%d", &v);
if (v == 0)
break;
add(i, v);
}
}
for (int i = 1; i <= n; i++)
{
if (dfn[i] == 0)
tarjan(i);
}
for (int i = 1; i <= tot; i++)
{
Edge ei = e[i];
graph[SCCID[ei.from]][SCCID[ei.to]] = 1;
}
for (int i = 1; i <= idx; i++)
for (int j = i + 1; j <= idx; j++)
{
if (graph[i][j])
{
inDeg[j]++;
outDeg[i]++;
}
else if (graph[j][i])
{
inDeg[i]++;
outDeg[j]++;
}
}
int out = 0, in = 0;
for (int i = 1; i <= idx; i++)
{
if (outDeg[i] == 0)
out++;
if (inDeg[i] == 0)
in++;
}
printf("%d\n", in);
if (idx == 1)
{
printf("0");
}
else
{
printf("%d", max(in, out));
}
return 0;
}