题意:
给一个有向图,求至少添加多少条边能够变成强连通图(任意两点互相可达)。
思路:
其实是DAG的一个性质:对于一个有向无环图,若想让它成为强连通图,至少需要添加max(a, b)条边,其中a为入度为0的点的数量,b为出度为0的点的数量。
其实自己推一推也找不到反例,所以对有向图进行缩点后根据入度、出度直接判断即可。
代码:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int inf = 0x3f3f3f3f;
const ll mod = 1e9+7;
const ll bas = 10001;
const int maxn = 10005;
const int maxm = 100005;
struct node {int u, v, next;} edge[maxm];
int no, head[maxn];
int _index, low[maxn], dfn[maxn], S[maxn], top, vis[maxn];
int deg1[maxn], deg2[maxn];
int bcc[maxn], cnt;
queue<int> q;
int n, m;
set<ll> _hash;
inline void init()
{
no = 0; _hash.clear();
memset(head, -1, sizeof head);
_index = top = 0; cnt = 0;
memset(dfn, 0, sizeof dfn);
memset(bcc, 0, sizeof bcc);
memset(deg1, 0, sizeof deg1);
memset(deg2, 0, sizeof deg2);
}
inline void add(int u, int v)
{
edge[no].u = u, edge[no].v = v;
edge[no].next = head[u];
head[u] = no++;
}
void tarjan(int u)
{
dfn[u] = low[u] = ++_index;
S[++top] = u;
vis[u] = 1;
for(int k = head[u]; k != -1; k = edge[k].next)
{
int v = edge[k].v;
if(!dfn[v])
{
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if(vis[v]) low[u] = min(low[u], dfn[v]);
}
if(dfn[u] == low[u])
{
++cnt;
do
{
bcc[S[top]] = cnt;
vis[S[top--]] = 0;
}
while(S[top+1] != u);
}
}
int main()
{
int t, u, v;
scanf("%d", &t);
while(t--)
{
scanf("%d %d", &n, &m); init();
for(int i = 1; i <= m; ++i)
{
scanf("%d %d", &u, &v);
add(u, v);
}
for(int i = 1; i <= n; ++i)
if(!dfn[i]) tarjan(i);
for(int i = 0; i < no; ++i)
{
int u = edge[i].u, v = edge[i].v;
if(bcc[u] != bcc[v] && _hash.find(bas*bcc[u]+bcc[v]) == _hash.end())
{
_hash.insert(bas*bcc[u]+bcc[v]);
++deg1[bcc[v]]; ++deg2[bcc[u]];
}
}
int num1 = 0, num2 = 0;
for(int i = 1; i <= cnt; ++i)
{
if(deg1[i] == 0) ++num1;
if(deg2[i] == 0) ++num2;
}
if(cnt <= 1) puts("0");
else printf("%d\n", max(num1, num2));
}
return 0;
}
继续加油~