题目连接:http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=120
解析:因为图中有环,环可以看做一个点,所以利用强连通缩点之后,在一个有向无环图上就好弄了,只用找一下入度为零的点的个数 和 出度为零的点的个数,较大的就是所求解,因为要消除所有入度和出度为零的点,所以出度为零的点到入度为零的点建边,哪种度为零的点剩余,就从这些点随便建边,所以较大的就是结果。 很简单,有点罗嗦了,不过特别注意的是当图为仅一个强连通分量的是后应输出0,忘了处理,WA了一次。。。
代码如下:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
#define NN 105
#define MM 10005
#define CLR(arr,v) memset(arr,v,sizeof(arr))
int Connect[NN],H[NN],Num[MM],Next[MM],ConnectNum,Ind[NN],Low[NN],Stack[NN],top,pos,ind;
bool map[NN][NN],InStack[NN];
int InDegree[NN],OutDegree[NN];
void Clear()
{
top = pos = ind = 0;
ConnectNum = 0;
CLR(H,-1);
CLR(map,false); CLR(InStack,false);
CLR(InDegree,0);CLR(OutDegree,0);
}
void add(int u,int v)
{
Num[pos] = v;
Next[pos] = H[u];
H[u] = pos++;
}
void Dfs(int cur)
{
Ind[cur] = Low[cur] = ++ind;
Stack[top++] = cur;
InStack[cur] = true;
for(int i = H[cur];i != -1;i = Next[i])
{
if(!Ind[ Num[i] ])
{
Dfs(Num[i]);
Low[cur] = min(Low[cur],Low[ Num[i] ]);
}
else if(InStack[ Num[i] ])
Low[cur] = min(Low[cur],Ind[ Num[i] ]);
}
if(Low[cur] == Ind[cur])
{
int s;
++ConnectNum;
do{
s = Stack[--top];
InStack[s] = false;
Connect[s] = ConnectNum;
}while(s != cur);
}
}
int Tarjan(int n)
{
CLR(Ind,0);
CLR(Low,0);
for(int i = 1;i <= n;++i)
if(!Ind[i]) Dfs(i);
return ConnectNum;
}
int BuildGraph(int n)
{
CLR(map,false);
for(int i = 1;i <= n;++i)
{
for(int j = H[i];j != -1;j = Next[j])
if(Connect[i] != Connect[ Num[j] ])
{
map[ Connect[i] ][ Connect[Num[i]] ] = true;
InDegree[ Connect[Num[i]] ]++;
OutDegree[ Connect[i] ]++;
}
}
int In = 0,Out = 0;
if(ConnectNum != 1)
{
for(int i = 1;i <= ConnectNum;++i)
{
if(!InDegree[i]) In++;
if(!OutDegree[i]) Out++;
}
}
return max(In,Out);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
Clear();
int n,e;
scanf("%d",&n);
for(int i = 1;i <= n;++i)
{
while(true)
{
scanf("%d",&e);
if(!e) break;
map[i][e] = true;
}
}
for(int i = 1;i <= n;++i)
for(int j = 1;j <= n;++j)
if(map[i][j] && i!=j) add(i,j);
Tarjan(n);
printf("%d\n",BuildGraph(n));
}
return 0;
}
重写了一次,模块化更清晰一点:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define CLR(arr,v) memset(arr,v,sizeof(arr))
template<int MaxV,int MaxE>
class Graph{
public:
void Clear(){
pos = 0; CLR(H,-1);
}
void add(int u,int v,int c){
Num[pos] = v;
Len[pos] = c;
Next[pos] = H[u];
H[u] = pos++;
}
int H[MaxV],Num[MaxE],Len[MaxE],Next[MaxE],pos;
};
const int M = 105 ;
Graph<M,M*M> g;
int Connect[M],Low[M],Ind[M],Stack[M],InStack[M],ConnectNum,top,ind;
void Dfs(int cur)
{
Low[cur] = Ind[cur] = ++ind;
Stack[top++] = cur;
InStack[cur] = true;
for(int i = g.H[cur]; i != -1; i = g.Next[i])
{
if(!Ind[ g.Num[i] ])
{
Dfs(g.Num[i]);
Low[cur] = min(Low[cur],Low[ g.Num[i] ]);
}
else if(InStack[ g.Num[i] ])
{
Low[cur] = min(Low[cur],Ind[ g.Num[i] ]);
}
}
if(Low[cur] == Ind[cur])
{
ConnectNum++;
int s;
do{
s = Stack[--top];
Connect[s] = ConnectNum;
InStack[s] = false;
}while(cur != s);
}
}
int Tarjan(int n)
{
CLR(Ind,0);
CLR(InStack,false);
ConnectNum = top = ind = 0;
for(int i = 1;i <= n;++i)
if(!Ind[i]) Dfs(i);
return ConnectNum;
}
Graph<M,M*M> g1;
void BuiltGraph(int n)
{
g1.Clear();
for(int i = 1;i <= n;++i)
for(int j = g.H[i]; j != -1; j = g.Next[j])
if(Connect[i] != Connect[ g.Num[j] ])
g1.add(Connect[i],Connect[ g.Num[i] ],0);
}
bool Inzero[M],Outzero[M];
int Result(int n)
{
CLR(Inzero,false);
CLR(Outzero,false);
for(int i = 1;i <= n;++i)
for(int j = g1.H[i]; j != -1; j = g1.Next[j])
Outzero[i] = Inzero[ g1.Num[j] ] = true;
int In = 0,Out = 0;
for(int i = 1;i <= n;++i)
{
if(!Inzero[i]) In++;
if(!Outzero[i]) Out++;
}
if(n == 1) return 0;
return max(In,Out);
}
int main()
{
int T;
cin>>T;
while(T--)
{
g.Clear();
int n,t,ConNum = 0;
cin>>n;
for(int i = 1;i <= n;++i)
{
while(cin>>t && t)
g.add(i,t,0);
}
ConNum = Tarjan(n);
BuiltGraph(n);
cout<<Result(ConNum)<<endl;
}
return 0;
}