題目大意
給一張有向圖,求最少能用多少有向邊建圖使新圖的連通性和原圖相同。
思路
在圖中不存在環的情況下,當兩點間能間接到達時,可將兩點間的直接邊刪去,可用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;
}