题目大意
给一张有向图,求最少能用多少有向边建图使新图的连通性和原图相同。
思路
在图中不存在环的情况下,当两点间能间接到达时,可将两点间的直接边删去,可用floyd求最长路,当两点间的距离为1时,则两点间不能间接到达,只能建边。
可用tarjan缩点将有环图转为DAG,在缩点后的图上跑floyd即可。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <stack>
#define INF 0x3f3f3f3f
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define mem(x, y) memset(x, y, sizeof(x))
#define MAXN 210
using namespace std;
int g[MAXN][MAXN], fld[MAXN][MAXN], d[MAXN][MAXN], n;
int dfn[MAXN], low[MAXN];///dfn[]表示深搜的步数,low[u]表示u或u的子树能够追溯到的最早的栈中节点的次序号
int sccno[MAXN];///缩点数组,表示某个点对应的缩点值
int step;
int scc_cnt;///强连通分量个数
vector<int> scc[MAXN];///得出来的缩点,scc[i]里面存i这个缩点具体缩了哪些点
stack<int> S;
void dfs(int u)
{
dfn[u] = low[u] = ++step;
S.push(u);
for (int i = 1; i <= n; i++)
{
if (i == u || !g[u][i])
continue;
int v = i;
if (!dfn[v])
{
dfs(v);
low[u] = min(low[u], low[v]);
}
else if (!sccno[v])
low[u] = min(low[u], dfn[v]);
}
if (low[u] == dfn[u])
{
scc_cnt += 1;
scc[scc_cnt].clear();
while(1)
{
int x = S.top();
S.pop();
if (sccno[x] != scc_cnt) scc[scc_cnt].push_back(x);
sccno[x] = scc_cnt;
if (x == u) break;
}
}
}
void tarjan(int n)
{
memset(sccno, 0, sizeof(sccno));
memset(dfn, 0, sizeof(dfn));
while (!S.empty())
S.pop();
for (int i = 0; i < MAXN; i++)
scc[i].clear();
step = scc_cnt = 0;
for (int i = 1; i <=n; i++)
if (!dfn[i]) dfs(i);
}
void floyd()
{
for (int k = 1; k <= scc_cnt; k++)
{
for (int i = 1; i <= scc_cnt; i++)
{
if (i == k)
continue;
for (int j = 1; j <= scc_cnt; j++)
{
if (i == j || j == k)
continue;
if (fld[i][k] && fld[k][j])
fld[i][j] = MAX(fld[i][k] + fld[k][j], fld[i][j]);
}
}
}
int ans = 0;
for (int i = 1; i <= scc_cnt; i++)
{
for (int j = 1; j <= scc_cnt; j++)
{
if (i == j)
continue;
if (fld[i][j] == 1)
ans++;
}
}
for (int i = 1; i <= scc_cnt; i++)
if (scc[i].size() > 1)
ans += scc[i].size();
printf("%d\n", ans);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif // ONLINE_JUDGE
while (scanf("%d", &n) != EOF)
{
mem(g, 0);
mem(fld, 0);
mem(d, 0);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
scanf("%d", &g[i][j]);
tarjan(n);
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (g[i][j])
fld[sccno[i]][sccno[j]] = 1;
}
}
floyd();
}
return 0;
}