題意:對於一個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; }