Copy:
题目大意:给出一张有向图G,求一个结点数最大的结点集,使得该点集中任意两个结点u和v满足:
要么u可到达v,要么v可以到达u(u和v互相可达也可以)
解题思路:u和v相互可达的时候,就是两个结点在同一个强连通分量内的时候
首先要保证集合里面的点可达:强连通分量就满足集合内的点都相互可达。所以第一件事就是找出所有的强连通分量,并统计出每个强连通分量内的结点数
然后找出每个强连通分量之间的关系,也就是找出两个强连通分量之间的桥,连接可连接的强连通分量
最后将每个强连通分量收缩,得到SCC图。此时的SCC图就变成了一个DAG,所以题目就转成用DP求DAG了
#include <stack> #include <cstdio> #include <cstring> #include <algorithm> #define N 1010 #define M 50010 using namespace std; struct Edge { int to, next; }; Edge E[M]; stack<int> S; int linklow[N], head[N], pre[N], num[N], sccno[N], dp[N]; int tot, dfs_clock, scc_cnt, n, m; bool link[N][N]; void addEdge(int from, int to) { E[tot].to=to; E[tot].next=head[from]; head[from]=tot++; } void dfs(int u) { linklow[u]=pre[u]= ++dfs_clock; S.push(u); for(int i=head[u]; i!=-1; i=E[i].next) { int v=E[i].to; if(!pre[v]) { dfs(v); linklow[u]=min(linklow[u], linklow[v]); } else if(!sccno[v]) { linklow[u]=min(linklow[u], pre[v]); } } if(pre[u]==linklow[u]) { scc_cnt++; num[scc_cnt]=0; while(1) { int x=S.top(); S.pop(); num[scc_cnt]++; sccno[x]=scc_cnt; if(x==u) break; } } } void find_SCC() { memset(pre, 0, sizeof(pre)); memset(sccno, 0, sizeof(sccno)); dfs_clock=scc_cnt=0; for(int i=0; i<= n; i++) if(!pre[i]) dfs(i); } int DP(int u) { if(dp[u]) return dp[u]; int Max=0; for(int i=1; i<=scc_cnt; i++) //找桥 ; if(i != u && link[u][i]) Max=max(Max, DP(i)); return dp[u]=Max+num[u]; } void init() { memset(head, -1, sizeof(head)); tot=0; scanf("%d%d", &n, &m); if(n==0) { printf("0\n"); return; } int u, v; for(int i=0; i<m; i++) { scanf("%d%d", &u, &v); addEdge(u, v); } find_SCC(); memset(link, 0, sizeof(link)); for(u=0; u<=n; u++) { for(int i=head[u]; i!=-1; i=E[i].next) { //DAG ; v=E[i].to; if(sccno[u] != sccno[v]) link[sccno[u]][sccno[v]]=true; } } memset(dp, 0, sizeof(dp)); int Max=0; for(int i=1; i<= scc_cnt; i++) { Max=max(Max, DP(i)); } printf("%d\n", Max); }
int main() { int T; scanf("%d", &T); while(T--) { init(); } return 0; }
浙公网安备 33010602011771号