CodeForces 506B/505D Mr. Kitayuta's Technology
Portal:http://codeforces.com/problemset/problem/506/B
http://codeforces.com/problemset/problem/505/D
好题
给n个城市,m条有向边,求出最少的有向边使得其构成的图与原图等势
对于每个连通分量:
如果无环,那么只需要需要n-1条边完成联通
如果有环,则只需要n条边完成联通
所以这题只要判下连通分量,再看有几个连通分量有环即可
解法一:无向图遍历求强连通分量再把强连通分量所代表的联通分量dfs判环,如下
Memory: 10440 KB | Time: 498 MS |
1 #include<iostream> 2 #include<algorithm> 3 #include<set> 4 #include<cstdio> 5 #include<cstdlib> 6 #include<cmath> 7 #include<vector> 8 using namespace std; 9 #define FOR(i,j,k) for(int i=j;i<=k;i++) 10 #define FORD(i,j,k) for(int i=j;i>=k;i--) 11 #define LL long long 12 #define SZ(x) int(x.size()) 13 #define maxn 100010 14 int n,m,x,y; 15 bool circle=true; 16 int vis[maxn],vis2[maxn]; 17 vector<int> neg[maxn],adj[maxn]; 18 vector<int> dot; 19 void ndfs(int start) 20 { 21 dot.push_back(start); 22 vis[start]=1; 23 FOR(i,0,SZ(neg[start])-1) 24 { 25 if(!vis[neg[start][i]]) ndfs(neg[start][i]); 26 27 } 28 return; 29 } 30 void adfs(int start) 31 { 32 vis2[start]=1; 33 FOR(i,0,SZ(adj[start])-1) 34 { 35 if(!vis2[adj[start][i]]) adfs(adj[start][i]); 36 else if(vis2[adj[start][i]]==1)circle=false; 37 } 38 vis2[start]=2; 39 return; 40 } 41 int main() 42 { 43 cin>>n>>m; 44 FOR(i,1,m) 45 { 46 cin>>x>>y; 47 neg[x].push_back(y); 48 neg[y].push_back(x); 49 adj[x].push_back(y); 50 } 51 int ans=n; 52 FOR(i,1,n) 53 { 54 circle=1; 55 if(!vis[i]) 56 { 57 dot.clear(); 58 ndfs(i); 59 FOR(i,0,SZ(dot)-1) 60 if (!vis2[dot[i]]) adfs(dot[i]); 61 ans-=circle; 62 } 63 } 64 cout<<ans<<endl; 65 return 0; 66 }
解法二:在无向图中维护并查集求强连通分量再把强连通分量所代表的联通分量用拓扑排序判环,如下
Memory: 7820 KB | Time: 514 MS |
1 #include<iostream> 2 #include<algorithm> 3 #include<set> 4 #include<cstdio> 5 #include<cstdlib> 6 #include<cmath> 7 #include<vector> 8 using namespace std; 9 #define FOR(i,j,k) for(int i=j;i<=k;i++) 10 #define FORD(i,j,k) for(int i=j;i>=k;i--) 11 #define LL long long 12 #define SZ(x) int(x.size()) 13 #define maxn 100010 14 vector<int> G[maxn]; 15 int n,m,x,y,T; 16 int father[maxn],val[maxn],circle[maxn],vis[maxn],clock[maxn]; 17 int setfind(int x) 18 { 19 int fa=father[x]; 20 if(fa==x) return x; 21 else return father[x]=setfind(fa); 22 } 23 void setunion(int x,int y) 24 { 25 int X=setfind(x); 26 int Y=setfind(y); 27 if(X==Y) return; 28 if(val[X]>val[Y]) father[Y]=X; 29 else father[X]=Y; 30 if(val[X]==val[Y]) val[X]++; 31 return; 32 } 33 void dfs(int start) 34 { 35 vis[start]=1; 36 FOR(i,0,SZ(G[start])-1) 37 if(!vis[G[start][i]]) dfs(G[start][i]); 38 clock[start]=++T; 39 return; 40 } 41 int main() 42 { 43 cin>>n>>m; 44 FOR(i,1,n) 45 {father[i]=i;val[i]=1;} 46 FOR(i,1,m) 47 { 48 cin>>x>>y; 49 G[x].push_back(y); 50 setunion(x,y); 51 } 52 FOR(i,1,n) 53 if(!vis[i]) dfs(i); 54 FOR(i,1,n) 55 FOR(j,0,SZ(G[i])-1) 56 if(clock[i]<clock[G[i][j]]) circle[setfind(i)]=1; 57 int ans=n; 58 FOR(i,1,n) 59 if(i==setfind(i))if(!circle[setfind(i)]) ans--; 60 cout<<ans<<endl; 61 return 0; 62 }
其实求连通分量还可以用染色
判有向环可以用并查集乱搞
反正就是怎么搞都能过