题目
问题描述:
兴趣小组的同学来自各个学校,为了增加友谊,晚会上又进行了一个传话游戏,如果a认识b,那么a收到某个消息,就会把这个消息传给b,以及所有a认识的人。
如果a认识b,b不一定认识a。
所有人从1到n编号,给出所有’认识’关系,问如果i发布一条新消息,那么会不会经过若干次传话后,这个消息传回给了i(1in)。
输入格式:
第一行是两个数n(n<1,000)和m(m<10,000),两数之间有一个空格,表示人数和认识关系数。
接下来的m行,每行两个数a和b,表示a认识b。(1a,bn)。认识关系可能会重复给出,但一行的两个数不会相同。
输出格式:
一共有n行,每行一个字符T或F。第i行如果是T,表示i发出一条新消息会传回给i;如果是F,表示i发出一条新消息不会传回给i。
样例输入输出:
message.in
4 6
1 2
2 3
4 1
3 1
1 3
2 3
message.out
T
T
T
F
分析
这道题明显就是要找一个关系环,也就是找一个人到另一个人最后回到最初那个人的环。找环显然用拓扑。这道题当中用了一个小技巧,我们用了两次相对的循环,一次针对入度,一次针对出度,然后没有vis标记过的就是在环上的点。(很聪明的办法)为什么会用到这样双向的拓扑,是因为如果只按照入度来做,就有可能有被包围在环当中的点被忽略了,所以从出度的角度由内向外再扫一遍保证答案正确。
注意数据大小!别乱开数组大小!当主程序return一个很大的值的时候注意检查数组大小!
程序
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int MAXN = 1005; 4 int n, m, u, v, dg[MAXN], og[MAXN]; 5 vector<int> a[1005],b[1005]; 6 bool g[MAXN][MAXN], vis[MAXN]; 7 queue<int> S; 8 int main() 9 { 10 freopen("message.in","r",stdin); 11 freopen("message.out","w",stdout); 12 cin >> n >> m; 13 for (int i = 1; i <= m; i++) 14 { 15 scanf("%d%d",&u,&v); 16 if (!g[u][v]) 17 { 18 dg[v]++, og[u]++; 19 g[u][v] = true; 20 a[u].push_back(v); 21 b[v].push_back(u); 22 } 23 } 24 for (int i = 1; i <= n; i++) 25 if (!dg[i]) 26 S.push(i); 27 while (!S.empty()) 28 { 29 int temp = S.front(); 30 S.pop(); 31 vis[temp] = true; 32 for (int i = 0; i < a[temp].size(); i++) 33 { 34 dg[a[temp][i]]--; 35 if (!dg[a[temp][i]]) 36 S.push(a[temp][i]); 37 } 38 } 39 for (int i = 1; i <= n; i++) 40 if (!og[i]) 41 S.push(i); 42 while (!S.empty()) 43 { 44 int temp = S.front(); 45 S.pop(); 46 vis[temp] = true; 47 for (int i = 0; i < b[temp].size(); i++) 48 { 49 og[b[temp][i]]--; 50 if (!og[b[temp][i]]) 51 S.push(b[temp][i]); 52 } 53 } 54 for (int i = 1; i <= n; i++) 55 if (!vis[i]) 56 printf("T\n"); 57 else 58 printf("F\n"); 59 return 0; 60 }