hdu3836Equivalent Sets(有向图需要添加至少几条边使得其成为强连通图)
题目描述:
To prove two sets A and B are equivalent, we can first prove A is a subset of B, and then prove B is a subset of A, so finally we got that these two sets are equivalent.
You are to prove N sets are equivalent, using the method above: in each step you can prove a set X is a subset of another set Y, and there are also some sets that are already proven to be subsets of some other sets.
Now you want to know the minimum steps needed to get the problem proved.
题意:给咱们一个有向图,求需要添加至少几条边使得其成为强连通图。
思路:还是先缩点,剩下的就是无环的图,题目要求就是添加几条边使得所有点在环内(强连通),
我最开始以为只需要找入读为0的点的个数就是结果,比如给我们1-2,3-4,我们就只需要最多两条边就行
然鹅我错了,如果是1-2,1-3呢,答案是两条边,入读为0的点只有1一个,但是你基本也已经可以猜出来了
答案应该是入读为0的点的个数与出度为0的点的个数的最大值。
注意特殊的图,如果最开始就给我们的是一个强连通图,就不用加边了。
AC代码:
#include<bits/stdc++.h> using namespace std; const int maxn = 50100; typedef long long ll; struct edge { int f, t, nxt; }e[maxn << 1]; int hd[maxn], tot; void add(int f, int t) { e[++tot] = { f,t,hd[f] }; hd[f] = tot; } int n, m; int low[maxn], dfn[maxn], cnt; int stk[maxn], sk, instk[maxn]; int col[maxn], colnum,indu[maxn],outdu[maxn]; void init() { tot = cnt =colnum= 0; memset(hd, 0, sizeof(hd)); memset(dfn, 0, sizeof(dfn)); memset(col, 0, sizeof(col)); memset(indu, 0, sizeof(indu)); memset(outdu, 0, sizeof(outdu)); } void tarjan(int u) {//缩点模板 low[u] = dfn[u] = ++cnt; stk[++sk] = u; instk[u] = 1; for (int i = hd[u]; i; i = e[i].nxt) { int v = e[i].t; if (!dfn[v]) { tarjan(v); low[u] = min(low[u], low[v]); } else if (instk[v]) { low[u] = min(low[u], dfn[v]); } } if (low[u] == dfn[u]) { colnum++;//强连通分量个数 while (1) { int v = stk[sk--]; instk[v] = 0; col[v] = colnum;//标记属于那个强连通分量 if (v == u)break; } } } int main() { //freopen("test.txt", "r", stdin); while (~scanf("%d%d", &n, &m)) { init(); for (int i = 1; i <= m;i++) { int a, b; scanf("%d%d", &a, &b); add(a, b); } for (int i = 1; i <= n; i ++) { if (!dfn[i]) { tarjan(i); } } if (colnum == 1) {//注意如果最开始图就是一个环,就不需要加边了 printf("0\n"); continue; } for (int i = 1; i <= m; i++) { int u = e[i].f, v = e[i].t; if (col[u] != col[v]) { //找出缩点后的入读和出度 indu[col[v]]++; outdu[col[u]]++; } } int ans1 = 0,ans2=0; for (int i = 1; i <= colnum; i++) { if (indu[i] == 0)ans1++; if (outdu[i] == 0)ans2++; } printf("%d\n",max(ans1,ans2)); //结果就是入读为0的点的个数和出度为0的点的个数的最大值,可以自己想一想或者画个图 } return 0; }