强连通图<一>

//大致思路:利用vector存图,同时把反图存好,接着利用深搜把后序的序列得到
//接着是按照后序的顺序深搜反图,并且在该点被走过的前提下将图中可以一次性可以走的点放入数组
//存放 也就是用数组f[x] = y, x是每次遍历到的点,具体的值y是第一个点(找到连通图第一个点)的值,这样该图的所有强连通图都得到了
题目连接

# include 
# include 
# include 
# include 
# include 
const int maxn = 300;
bool vis[maxn];
int hou[maxn];
int f[maxn];
int n, m;
int sum_[maxn];
using namespace std;
struct node {
	int to;
}temp;
int cnt = 0;
vector<node>mp[maxn];
vector<node>remp[maxn];
void dfs(int r) {
	for (int i = 0;i < mp[r].size();i++) {
		temp = mp[r][i];
		vis[r] = 1;
		if (vis[temp.to] == false) {
			vis[temp.to] = true;
			dfs(temp.to);
		}
	}
	hou[cnt++] = r;//后序
}
void redfs(int r, int y) {
	for (int i = 0;i < remp[r].size();i++) {
		vis[r] = 0;
		temp = remp[r][i];
		if (vis[temp.to] == true)
		{
			vis[temp.to] = false;
			
			f[temp.to] = y;//一个连通图的每一个点存放的值是该图的第一个点的值
			redfs(temp.to, y);//递归位置很重要!
		}
	}
}
void kslj() {
	for (int i = 1;i <= n;i++) {
		if(vis[i]==false)
		dfs(i);
	}
//	for (int i = 0;i < cnt;i++) {
	//	cout << hou[i] << " ";
	//}
	for (int i = cnt - 1;i >= 0;i--) {
		redfs(hou[i], hou[i]);
	}
}
int main() {
	cin >> n >> m;
	memset(vis, false, sizeof(vis));
	memset(f, 0, sizeof(f));
	memset(hou, 0, sizeof(hou));
	while(m--) {
		int x1, x2;
		cin >> x1 >> x2;
		temp.to = x2;
		mp[x1].push_back(temp);
		temp.to = x1;
		remp[x2].push_back(temp);//存反图
	}
	kslj();
	for (int i = 1;i <= n;i++) {
		sum_[f[i]]++;
	}
	int sum = 0;
	for (int i = 1;i <= n;i++) {
		sum += sum_[i]*(sum_[i]-1)/2;
	}
	cout << sum << endl;
	/*sets;此处用于找到有几个强连通图
	for (int i = 1;i <= n;i++) {
		s.insert(f[i]);
		cout << f[i] << " ";
	}*/
	//cout << s.size() << endl;
}

以下是学习代码(有问题的)

#include 
#include 
#include 
#define _  ios_base::sync_with_stdio(0),cin.tie(0)
using namespace std;

struct Edge{
	int to;
}edge; //作为临时的储存边的变量
const int maxn=305;
vector<Edge> mp[maxn];//放图
vector<Edge> remp[maxn];//放反图
int n,m;
bool vis[maxn];//标记是否走过 以及第一遍已经走过了
int f[maxn];//下标是访问节点,具体数值是第一次访问节点的值,从而确定强连通有几个
int p[maxn];//后序
int cnt=0;
void dfs(int x){
vis[x] = 1;
	for(int i=0;i<mp[x].size();i++){
		edge=mp[x][i];
		int v=edge.to;
		if(!vis[v]){//当一次都没有访问过进入循环
			vis[v]=true;//进入后改变
			dfs(v);
		}
	}
	p[cnt++]=x;
}
void redfs(int x,int y){//此处的y是一个强连通图中进入的第一个结点,
	for(int i=0;i<remp[x].size();i++){
	vis[x] = 0;
		edge=remp[x][i];
		int v=edge.to;
		if(vis[v]){//当被走过一遍的点可直接进入下一次便利(所有点在第一遍都走过),
			vis[v]=false;
			f[v]=y;//缩点,该图的所有点的值都是该图进入的第一个数
			redfs(v,y);
		}
	}
}
void kosaraju(){
	for(int i=1;i<=n;i++){//保证所有点都不访问
		if(!vis[i])
			dfs(i);
	}
//	for(int i=0;i
//	cout<
//	cout<
	for(int j=cnt-1;j>=0;j--){
		if(vis[p[j]])//反方向访问,按后序的顺序深搜该图
			redfs(p[j],p[j]);//第二个变量表示第一个访问进去的点是谁
	}
}
int main(){
	cin>>n>>m;
	memset(vis,false,sizeof(vis));
	memset(f,-1,sizeof(f));
	memset(p,-1,sizeof(p));
	cnt=0;
	while(m--){
		int u,v;
		cin>>u>>v;
		edge.to=v;
		mp[u].push_back(edge);
		edge.to=u;
		remp[v].push_back(edge);
	} 
	kosaraju();
/*求对点个数,强连通图中的边数*/
	vector<int>ans(n+1);
	for(int i=1;i<=n;i++){
		ans[f[i]]++;//数组中放一个强连通图中有几个点数
	} 
	int sum=0;
	for(int i=1;i<=n;i++){
		sum+=(ans[i]*(ans[i]-1))/2;//根据连通图的点数确实一个连通图有多少边
//		cout<
	}
	cout<<sum;
/*查找强连通分量的个数      setans;
	for(int i=1;i<=n;i++){	
		ans.insert(f[i]);
	}
	cout<
	return 0;
}
/*
9 12
1 2
1 4
2 3
3 1
4 5
5 6
5 7
5 8
6 4
7 6
8 9
9 8
*/

你可能感兴趣的:(暑训)