题目:
http://acm.hdu.edu.cn/showproblem.php?pid=1827
用tarjan算法,先对各个极大强连通图进行缩点同时记录缩点中话费最少的值,然后看每一个缩点的入度,如果入度为0,那么说明需要给这个缩点打电话。
源代码:
1 #include <iostream> 2 #include<vector> 3 #include<stdio.h> 4 #include<cstring> 5 #include<algorithm> 6 #define INF 100000000 7 using namespace std; 8 vector<int> map[20005]; 9 10 int n,m,now,cnt,sum1,sum2,top,num,ans; 11 int dfn[20005],low[20005],stack[20005],Stack[20005],belong[20005],to[20005],c[10005]; 12 int mini[10005]; 13 void tarjan(int a){ 14 dfn[a]=low[a]=++now; 15 int u; 16 stack[++top]=a; //如果用标准库中自带的stack会超时~~~,用这个数组模拟栈 17 Stack[a]=1; //注意我用大写Stack表示是否在栈中,小写s模拟栈 18 for(int i=0;i<map[a].size();i++){ 19 u=map[a][i]; 20 if(!dfn[u]){ 21 tarjan(u); 22 low[a]=min(low[a],low[u]); 23 } 24 else if(Stack[u]) 25 low[a]=min(low[a],dfn[u]); 26 } 27 if(low[a]==dfn[a]){ 28 int temp; 29 cnt++; 30 do{ 31 temp=stack[top--]; 32 belong[temp]=cnt; 33 if(c[temp]<mini[cnt]) 34 mini[cnt]=c[temp]; 35 Stack[temp]=0; 36 }while(temp!=a); 37 } 38 } 39 40 void fun(){ 41 for(int i=1;i<=n;i++){ 42 for(int j=0;j<map[i].size();j++){ 43 int k=map[i][j]; 44 if(belong[i]!=belong[k]){ //相邻两个点不属于同一缩点 45 to[belong[k]]++; 46 } 47 } 48 } 49 for(int i=1;i<=cnt;i++){ 50 if(!to[i]){ //入度为0 51 ans+=mini[i]; 52 num++;} 53 } 54 return; 55 } 56 57 int main() 58 { 59 while(scanf("%d %d",&n,&m)!=EOF){ 60 61 cnt=now=sum1=sum2=0; 62 top=ans=0; 63 num=0; 64 memset(dfn,0,sizeof(dfn)); //时间戳 65 memset(low,0,sizeof(low)); //极大强连通图的最小时间 66 memset(Stack,0,sizeof(Stack)); //是否在栈中 67 memset(to,0,sizeof(to)); //缩点的入度 68 for(int i=1;i<=n;i++){ 69 scanf("%d",&c[i]); //每个点的话费 70 map[i].clear(); //这个一定要写啊,我老是忘了写这个 71 mini[i]=INF; //每个缩点的最小话费 72 } 73 for(int i=1;i<=m;i++){ 74 int a,b; 75 scanf("%d %d",&a,&b); 76 map[a].push_back(b); 77 } 78 for(int i=1;i<=n;i++){ 79 if(!dfn[i]) 80 tarjan(i); 81 } 82 fun(); 83 printf("%d %d\n",num,ans); 84 } 85 return 0; 86 }