用tarjan算法,先对各个极大强连通图进行缩点同时记录缩点中话费最少的值,然后看每一个缩点的入度,如果入度为0,那么说明需要给这个缩点打电话。
#include <iostream> #include<vector> #include<stdio.h> #include<cstring> #include<algorithm> #define INF 100000000 using namespace std; vector<int> map[20005]; int n,m,now,cnt,sum1,sum2,top,num,ans; int dfn[20005],low[20005],stack[20005],Stack[20005],belong[20005],to[20005],c[10005]; int mini[10005]; void tarjan(int a){ dfn[a]=low[a]=++now; int u; stack[++top]=a; //如果用标准库中自带的stack会超时,用这个数组模拟栈 Stack[a]=1; //注意我用大写Stack表示是否在栈中,小写s模拟栈 for(int i=0;i<map[a].size();i++){ u=map[a][i]; if(!dfn[u]){ tarjan(u); low[a]=min(low[a],low[u]); } else if(Stack[u]) low[a]=min(low[a],dfn[u]); } if(low[a]==dfn[a]){ int temp; cnt++; do{ temp=stack[top--]; belong[temp]=cnt; if(c[temp]<mini[cnt]) mini[cnt]=c[temp]; Stack[temp]=0; }while(temp!=a); } } void fun(){ for(int i=1;i<=n;i++){ for(int j=0;j<map[i].size();j++){ int k=map[i][j]; if(belong[i]!=belong[k]){ //相邻两个点不属于同一缩点 to[belong[k]]++; } } } for(int i=1;i<=cnt;i++){ if(!to[i]){ //入度为0 ans+=mini[i]; num++;} } return; } int main() { while(scanf("%d %d",&n,&m)!=EOF){ cnt=now=sum1=sum2=0; top=ans=0; num=0; memset(dfn,0,sizeof(dfn)); //时间戳 memset(low,0,sizeof(low)); //极大强连通图的最小时间 memset(Stack,0,sizeof(Stack)); //是否在栈中 memset(to,0,sizeof(to)); //缩点的入度 for(int i=1;i<=n;i++){ scanf("%d",&c[i]); //每个点的话费 map[i].clear(); //多组数据一定要写 mini[i]=INF; //每个缩点的最小话费 } for(int i=1;i<=m;i++){ int a,b; scanf("%d %d",&a,&b); map[a].push_back(b); } for(int i=1;i<=n;i++){ if(!dfn[i]) tarjan(i); } fun(); printf("%d %d\n",num,ans); } return 0; }