题意:一张有向图,一问至少给几个点发送软件,才能让所有点都能收到软件;二问是至少添加几条边才能让整个图是一个连通分量;
分析:一般求连通分量都会求缩点,在这里缩点之后,生成一张新的图,在新的图中求每一个点的出度,入度。答案就是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 }
时间: 2024-10-27 05:25:55