思路:先有向图缩点,成有向树,找入度为0的点即可。
下面给出有向图缩点方法:
用一个数组SCC记录即可,重新编号,1....num,具体方法如下代码详见。
#include<iostream> #include<cstdio> #include<vector> #include<stack> using namespace std; int n,m; vector<vector<int> >v(10010); int vis[10010];int dfn[10010];int low[10010];int times=0; int num=0; int instack[10010];stack<int>s; //有向图的连通性,用栈。 int scc[1010]; int w[1010]; int neww[1010]; int ind[1010]; void tarjarn(int u) { dfn[u]=low[u]=++times; instack[u]=1; s.push(u); for(int i=0;i<v[u].size();i++) { int vv=v[u][i]; if(!vis[vv]) { vis[vv]=1; tarjarn(vv); if(low[vv]<low[u])low[u]=low[vv]; //孩子可以到达,我必然也可以到达。 } else if(instack[vv]) //注意,更新时要有在栈中条件,代表该孩子(其实是祖先)我能到达,而且是属于当前SCC。 { if(dfn[vv]<low[u])low[u]=dfn[vv]; //能到达的祖先,所以更新。 } } if(dfn[u]==low[u]) { num++;int cur; int min=w[u]; //找这个SCC权最小的权。 do { cur=s.top(); if(w[cur]<min)min=w[cur]; s.pop(); instack[cur]=0; scc[cur]=num; }while(cur!=u); neww[num]=min; } } int main() { while(~scanf("%d %d",&n,&m)) { int ta,tb; for(int i=0;i<=n;i++) { v[i].clear(); neww[i]=ind[i]=w[i]=instack[i]=dfn[i]=low[i]=vis[i]=0; } times=0; for(int i=1;i<=n;i++) { scanf("%d",&w[i]); } for(int i=0;i<m;i++) { scanf("%d %d",&ta,&tb); v[ta].push_back(tb); } num=0; for(int i=1;i<=n;i++) { if(!vis[i]) { vis[i]=1; tarjarn(i); } } int sum=0;int count=0; for(int i=1;i<=n;i++) //遍历边。 { for(int j=0;j<v[i].size();j++) { if(scc[i]!=scc[v[i][j]]) { ind[scc[v[i][j]]]++; } } } for(int i=1;i<=num;i++) { if(ind[i]==0) { sum+=neww[i]; count++; } } printf("%d %d ",count, sum); } return 0; }