SSL 2344 刻录光盘
题目大意
简单来说,就是给出几个有向边,求连通块的数目。
算法选择
200的数据,O(n^3)的算法也轻松过,所以用Floyd或其他更快的算法都行
Code:这里给出Floyd、Kosaraju和Tarjan算法
Floyd
思路
先用Floyd求出这些员工的互相连通情况,然后用并查集把i的祖先设定为能够给i光盘的点上,最后求出有几个点的父节点仍然是他自己,就能求出来了;
#include<iostream>
#include<cstdio>
using namespace std;
int n,father[210],ans=0;
bool a[210][210];
int main(){
ios::sync_with_stdio(false);
cin>>n;
for(int i=1,t;i<=n;i++)while(cin>>t and t!=0) a[i][t]=true;
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
a[i][j]=a[i][j] or (a[i][k] and a[k][j]);//Floyd大法
for(int i=1;i<=n;i++) father[i]=i;//初值,每个结点最初的祖先就是他们自己
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(a[i][j]) father[j]=father[i];//如果可以抵达,则终点的祖先就等于起点的祖先
for(int i=1;i<=n;i++)if(father[i]==i) ans++;//有几个结点的祖先仍然是自己,就有几个连通块
cout<<ans;
return 0;
}
Kosaraju
思路
Kosaraju模板;
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,m,ans,f[210];
bool a[210][210],vis[210];
void d1(int x){
vis[x]=1;
for(int i=1;i<=n;i++)
if(a[x][i] and !vis[i]) d1(i);
f[++m]=x;
}
void d2(int x){
vis[x]=1;
for (int i=1;i<=n;i++)
if (a[x][i] and !vis[i]) d2(i);
}//d2相比d1少了一行f[++m]=x,因为留着的话会引发错乱;
int main(){
ios::sync_with_stdio(false);
cin>>n;
for(int i=1,t;i<=n;i++)while(cin>>t,t) a[i][t]=true;
for(int i=1;i<=n;i++){if(!vis[i]) d1(i);}memset(vis,0,sizeof(vis));//算法第一步求退出顺序
for(int i=m;i>0;i--){if(!vis[f[i]]) ans++;d2(f[i]);}//算法第二步累加
cout<<ans;//输出
return 0;
}
tarjan
#include<iostream>
#include<cstdio>
#define sco 100010
using namespace std;
struct node{int u,v,next;}e[sco];
int st[sco],top=0;
int n,k,cnt=0,cum,dfn[sco],low[sco],vis[sco],ls[sco],cor[sco],rd[sco],ans;//ans是累加答案的变量
//n表示人数,e是邻接表,k、ls是邻接表的辅助变量,cnt、dfn、low、vis、cor,rd都是tarjan的辅助变量;
//其中,cnt表示第几次访问,dfn[i]表示i这个点是第几次访问的,low[u]=min(low[u],low[v]);
//cor是染色变量,把同一个强连通分量里的数字都染上同样的数字即cum;rd[i]储存i节点的入度;
void tarjan(int x){
dfn[x]=low[x]=++cnt;
st[++top]=x;vis[x]=1;
int t=ls[x];
while(t){
if(!dfn[e[t].v]){
tarjan(e[t].v);
low[x]=min(low[x],low[e[t].v]);
}
else if(vis[e[t].v]){
low[x]=min(low[x],low[e[t].v]);
}
t=e[t].next;
}
if(dfn[x]==low[x]){
vis[x]=0;
cor[x]=++cum;
do{
vis[st[top]]=0;
cor[st[top]]=cum;
}while(st[top--]!=x);
}
}
int main(){
ios::sync_with_stdio(false);
cin>>n;
for(int i=1,t;i<=n;i++){
while(cin>>t,t){
e[++k].u=i;e[k].v=t;e[k].next=ls[e[k].u];ls[e[k].u]=k;
}
}
for(int i=1;i<=n;i++)if(dfn[i]==0) tarjan(i);
for(int i=1;i<=k;i++)if(cor[e[i].u]!=cor[e[i].v]) rd[cor[e[i].v]]++;
for(int i=1;i<=cum;i++)if(!rd[i]) ans++;
cout<<ans;
return 0;
}