POJ 1236 SCC+缩点
题意:一张有向图,一问至少给几个点发送软件,才能让所有点都能收到软件;二问是至少添加几条边才能让整个图是一个连通分量;
分析:一般求连通分量都会求缩点,在这里缩点之后,生成一张新的图,在新的图中求每一个点的出度,入度。答案就是sum(入度=0),max(sum(出度 == 0),sum(入度 == 0));
注意:如果整张图本来就是一个强连通分量,需要特判。因为它出度,入度都等于0,即max(1,1) = 1,但是实际上不用再补充边了,应该是0,按照上面的分析答案就错了。
1 ///POJ1236
2 ///时间复杂度也是O(N+M)
3 #include <stdio.h>
4 #include <string.h>
5 #include <vector>
6 #include <stack>
7 #include <iostream>
8 #define repu(i,a,b) for(int i=a;i<b;i++)
9 using namespace std;
10 #define N 105 /// 题目中可能的最大点数
11 stack<int>sta; /// 存储已遍历的结点
12 vector<int>gra[N]; /// 邻接表表示图
13 int dfn[N]; /// 深度优先搜索访问次序
14 int low[N]; /// 能追溯到的最早的次序
15 int InStack[N];
16 /// 检查是否在栈中(2:在栈中,1:已访问,且不在栈中,0:不在)
17 vector<int> Component[N]; /// 获得强连通分量结果
18 int InComponent[N]; /// 记录每个点在第几号强连通分量里
19 int Index,ComponentNumber;/// 索引号,强连通分量个数
20 int n, m; /// 点数,边数
21 int d[N][N],chu[N],ru[N];
22
23 void init()///清空容器,数组
24 {
25 memset(dfn, 0, sizeof(dfn));
26 memset(low, 0, sizeof(low));
27 memset(chu, 0, sizeof(chu));
28 memset(ru, 0, sizeof(ru));
29 memset(InStack, 0, sizeof(InStack));
30 Index = ComponentNumber = 0;
31 for (int i = 1; i <= n; ++ i)
32 {
33 gra[i].clear();
34 Component[i].clear();
35 }
36 repu(i,1,n+1)
37 repu(j,1,n+1)
38 d[i][j] = 0;
39 while(!sta.empty())
40 sta.pop();
41 }
42 void tarjan(int u)
43 {
44 InStack[u] = 2;
45 low[u] = dfn[u] = ++ Index;
46 sta.push(u);///寻找u所在的强连通分量
47 for (int i = 0; i < gra[u].size(); ++ i)
48 {
49 int t = gra[u][i];
50 if (dfn[t] == 0)///不在的继续递归
51 {
52 tarjan(t);///递归到头了就
53 low[u] = min(low[u], low[t]);
54 }
55 else if (InStack[t] == 2)///在栈里
56 {
57 low[u] = min(low[u], dfn[t]);
58 }
59 }
60 if(low[u] == dfn[u])///sta出栈就是一个强连通分量的
61 {
62 ++ComponentNumber;///强连通分量个数
63 while (!sta.empty())
64 {
65 int j = sta.top();
66 sta.pop();
67 InStack[j] = 1;///已访问但不在栈中
68 Component[ComponentNumber].push_back(j);
69 ///用vector存储第ComponentNumber个强连通分量
70 InComponent[j]=ComponentNumber;
71 ///记录每个点在第几号强连通分量里
72 if (j == u)
73 break;
74 }
75 }
76 }
77 void input()
78 {
79 repu(i,1,n+1)
80 {
81 while(scanf("%d",&m) &&m)
82 d[i][m] = 1,gra[i].push_back(m);///有向图才有强连通分量
83 }
84 }
85
86 void solve(void)
87 {
88 for(int i=1; i<=n; i++)
89 if(!dfn[i])
90 tarjan(i);
91 if(ComponentNumber == 1)
92 {
93 printf("1\n0\n");
94 return;
95 }
96 ///缩点
97 for(int i=1; i<=ComponentNumber; i++)
98 {
99 for(int j = 0; j < Component[i].size(); j++)
100 {
101 for(int k = 1; k<=n; k++)
102 {
103 if(d[k][Component[i][j]] && k != Component[i][j])
104 {
105 int s = InComponent[k];
106 int t = InComponent[Component[i][j]];
107 if(s!=t)
108 {
109 chu[s]++;
110 ru[t]++;
111 }
112 }
113 }
114 }
115 }
116 int sum = 0,num = 0;
117 for(int i=1; i<=ComponentNumber; i++)
118 {
119 if(!chu[i])
120 sum++;
121 if(!ru[i])
122 num++;
123 }
124 printf("%d\n%d\n",num,max(sum,num));
125 }
126
127 int main()
128 {
129 while(~scanf("%d",&n))
130 {
131 init();
132 input();
133 solve();
134 /*每一个强连通分量的具体数字
135 for(int i = 1; i <= ComponentNumber; i++)
136 {
137 for(int j = 0; j < Component[i].size(); j++)
138 if(!j)
139 cout << Component[i][j];
140 else
141 cout <<"-->"<< Component[i][j];
142 cout<<endl;
143 }
144 */
145 }
146 return 0;
147 }
人生就像心电图,想要一帆风顺,除非game-over