把证明的关系看出一张图,最终就是要所有的点都在至少一个环中。环的判断和度数有关。
用tarjan找强连通分量,在一个强连通分量点已经等价缩点以后形成一个DAG,计算入度为0的点数a,
出度为0的b,取其中大的一个。特判强连通分量数为1的情况。
看懂tarjan算法以后还是比较简单的
#include<bits/stdc++.h> using namespace std; const int maxn = 2e4+5; const int maxm = 5e4+5; int head[maxn],nxt[maxm],to[maxm],ecnt; void addEdge(int u,int v) { nxt[ecnt] = head[u]; to[ecnt] = v; head[u] = ecnt++; } void initGraph(int n) { memset(head,-1,sizeof(int)*(n+1)); ecnt = 0; } int sccno[maxn],pre[maxn],low[maxn],dfs_clock,scc_cnt; stack<int> stk; void tarjan(int u) { pre[u] = low[u] = ++dfs_clock; stk.push(u); for(int i = head[u]; ~i; i = nxt[i]){ int v = to[i]; if(!pre[v]){ tarjan(v); low[u] = min(low[u],low[v]); }else if(!sccno[v]){ low[u] = min(low[u],pre[v]); } } if(low[u] == pre[u]){ scc_cnt++; while(stk.size()){ int x = stk.top(); stk.pop(); sccno[x] = scc_cnt; if(x == u) break; } } } void find_scc(int n) { memset(pre,0,sizeof(int)*(n+1)); memset(sccno,0,sizeof(int)*(n+1)); scc_cnt = dfs_clock = 0; for(int i = 0; i < n; i++){ if(!pre[i]) tarjan(i); } } int ind[maxn],outd[maxn]; int main() { //freopen("in.txt","r",stdin); int T; scanf("%d",&T); while(T--){ int n,m; scanf("%d%d",&n,&m); initGraph(n); for(int i = 0; i < m; i++){ int u,v; scanf("%d%d",&u,&v); addEdge(u-1,v-1); } find_scc(n); if(scc_cnt == 1) { printf("0 "); continue; } for(int i = 1; i <= scc_cnt; i++) ind[i] = outd[i] = 0; for(int u = 0; u < n; u++){ for(int i = head[u]; ~i; i = nxt[i]){ int v = to[i]; if(sccno[u] != sccno[v]) outd[sccno[u]]++,ind[sccno[v]]++; } } int a = 0,b = 0; for(int i = 1; i <= scc_cnt; i++){ if(!outd[i]) b++; if(!ind[i]) a++; } printf("%d ",max(a,b)); } return 0; }