1. 程式人生 > >間諜網路--tarjan 解題報告

間諜網路--tarjan 解題報告

一道比較裸的tarjan題,但由於第一次寫,還是wa了幾次

可以先dfs一遍,如果是NO就直接遍歷找出最小的點,return 0;
否則就tarjan一邊並把一個SCC裡cost最小的值min找到,SCC_cost[SCC_sum]=min,並縮點,最後找入度為零的點計算cost的和,輸出即可。

程式碼如下

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
const
int N=3005,M=10005; bool ss[N][N],pd[N],instack[N],vis[N]; int DFN[N],low[N],head[N],cost[N],SCC_num[N],SCC_cost[N],stack[N],edge_from[M],edge_to[M],rudu[N]; int num,SCC_sum,indexxx,top,n,n1,n2; struct Edge { int next,to; }s[M]; void add(int from,int to) { s[++num].next=head[from]; s[num].to=to; head[from]=num; return
; } void read(int &x) { x=0;char c=getchar();while(c<48||c>57) c=getchar();while(c>=48&&c<=57) x=x*10+c-48,c=getchar(); return ; } void dfs(int u) { pd[u]=1; for(int i=head[u];i;i=s[i].next) if(!pd[s[i].to]) dfs(s[i].to); return ; } void tarjan(int u) { instack[u]=vis[u]=1
; low[u]=DFN[u]=++indexxx; stack[++top]=u; for(int i=head[u];i;i=s[i].next) { int v=s[i].to; if(!DFN[v]) { tarjan(v); low[u]=min(low[u],low[v]); } else if(instack[v])low[u]=min(low[u],DFN[v]); } if(low[u]==DFN[u]) { SCC_sum++; int v=0; while(v!=u) v=stack[top--],instack[v]=0,SCC_num[v]=SCC_sum,SCC_cost[SCC_sum]=min(SCC_cost[SCC_sum],cost[v]); } return ; } int main() { memset(cost,127/3,sizeof(cost)); memset(SCC_cost,127/3,sizeof(SCC_cost)); int i,a,b; read(n),read(n1); for(i=1;i<=n1;i++) { read(a),read(b); cost[a]=b; } read(n2); for(i=1;i<=n2;i++) { read(a);read(b); edge_from[i]=a,edge_to[i]=b; add(a,b); } for(i=1;i<=n;i++) { if(!pd[i]&&cost[i]!=707406378) dfs(i); } for(i=1;i<=n;i++) { if(!pd[i]) { printf("NO\n%d\n",i); return 0; } } memset(pd,0,sizeof(pd)); for(i=1;i<=n;i++) if(!DFN[i]) tarjan(i),top=0; memset(s,0,sizeof(s)); memset(rudu,0,sizeof(rudu)); num=0; for(i=1;i<=n2;i++) { if(SCC_num[edge_from[i]]!=SCC_num[edge_to[i]]) rudu[SCC_num[edge_to[i]]]++; } int sum=0; for(i=1;i<=SCC_sum;i++) if(!rudu[i]) sum+=SCC_cost[i]; printf("YES\n%d\n",sum); return 0; }