题意:对于一个DAG,至少要选多少个点才能从这个点到所有点,至少要添加多少条边才能从任意一点到所有点。
思路:对于第一问,用Tarjan求出强联通分量,缩点后求出入度为0的点就是答案。
对于第二问,在缩点后求出入度为0,初度为0的个数,取最大值就是所要添加边的条数。
注意对强联通分量只有一个时的特判。
#include <iostream> #include <cstring> #include <algorithm> #include <cstdio> #include <vector> #include <stack> using namespace std; const int maxn = 110; vector<int>g[maxn]; int pre[maxn],low[maxn],sccno[maxn],dfs_clock,scc_cnt; int in[maxn],out[maxn],num[maxn]; stack<int>s; void dfs(int u) { pre[u] = low[u] = ++dfs_clock; s.push(u); for(int i = 0; i < g[u].size(); i++) { int v = g[u][i]; if(!pre[v]) { dfs(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++; for(;;) { int x = s.top(); s.pop(); sccno[x] = scc_cnt; if(x==u) break; } } } void find_scc(int n) { dfs_clock = scc_cnt = 0; memset(sccno,0,sizeof(sccno)); memset(pre,0,sizeof(pre)); memset(num,0,sizeof(num)); memset(in,0,sizeof(in)); memset(out,0,sizeof(out)); for(int i = 1; i <= n; i++) { if(!pre[i]) dfs(i); } } int main() { int n; int x; scanf("%d",&n); for(int i = 1; i <= n; i++) { while(scanf("%d",&x)&&x) { g[i].push_back(x); } } find_scc(n); for(int i = 1; i <= n; i++) { for(int j = 0; j < g[i].size(); j++) { int v = g[i][j]; if(sccno[i]!=sccno[v]) { in[sccno[v]]++; out[sccno[i]]++; } } } int cnt1 = 0,cnt2 = 0; for(int i = 1; i <= scc_cnt; i++) { if(in[i]==0)cnt1++; if(out[i]==0)cnt2++; } int ans = max(cnt1,cnt2); if(scc_cnt==1) { printf("1\n0\n"); } else { printf("%d\n%d\n",cnt1,ans); } return 0; }