<题目链接>
题目大意:
给你一张有向图 G,求一个结点数最大的结点集,使得该结点集中的任意两个结点 u 和 v 满足:要么 u 可以达 v,要么 v 可以达 u(u,v相互可达也行)。
解题分析:
该点集需满足两个要求:1.任意两点至少有一方能够到达另外一点;2.点数尽可能的多。
通过画图分析可以知道,对于那些强连通分量来说,要不就全部加入该点集,要不就全部不能加入,所以直接对原图进行缩点,进行重新构图。然后,根据重新构造的DAG图我们可以知道,要使该点集中任意两点至少有一方能够到达另外一点,并且要求点数尽可能的多,可以发现,其实就是让我们求缩点后的最长路。
1 #include <cstdio> 2 #include <cstring> 3 #include <vector> 4 #include <algorithm> 5 using namespace std; 6 7 #define pb push_back 8 #define clr(a,b) memset(a,b,sizeof(a)) 9 #define rep(i,s,t) for(int i=s;i<=t;i++) 10 const int N = 1e3+10,M = 5e4+10; 11 int n,m,tot,tott,top,scc; 12 int head[N],belong[N],dfn[N],low[N],stk[N],instk[N],dp[N],cnt[N]; 13 vector<int>G[N]; 14 struct Edge{ 15 int to,next; 16 }edge[M]; 17 void init(){ 18 rep(i,0,n)G[i].clear(); 19 tot=scc=top=tott=0; 20 clr(head,-1);clr(low,0);clr(dfn,0);clr(stk,0);clr(instk,0);clr(belong,0);clr(dp,0);clr(cnt,0); 21 } 22 void addedge(int u,int v){ 23 edge[++tot].to=v,edge[tot].next=head[u]; 24 head[u]=tot; 25 } 26 void Tarjan(int u){ //进行缩点 27 dfn[u]=low[u]=++tott; 28 stk[++top]=u;instk[u]=1; 29 for(int i=head[u];~i;i=edge[i].next){ 30 int v=edge[i].to; 31 if(!dfn[v]){ 32 Tarjan(v); 33 low[u]=min(low[u],low[v]); 34 }else if(instk[v])low[u]=min(low[u],dfn[v]); 35 } 36 if(dfn[u]==low[u]){ 37 scc++; 38 while(true){ 39 int v=stk[top--]; 40 instk[v]=0; 41 belong[v]=scc; 42 cnt[scc]++; 43 if(v==u)break; 44 } 45 } 46 } 47 //记忆化搜索求以u为起点的最长路,点权为"点"中点的个数 48 int DFS(int u){ 49 if(dp[u])return dp[u]; 50 int ans=cnt[u]; 51 for(int i=0;i<G[u].size();i++){ 52 int v=G[u][i]; 53 ans=max(ans,DFS(v)+cnt[u]); 54 } 55 return dp[u]=ans; 56 } 57 int main(){ 58 int T;scanf("%d",&T);while(T--){ 59 init(); 60 scanf("%d%d",&n,&m); 61 rep(i,1,m){ 62 int u,v;scanf("%d%d",&u,&v); 63 addedge(u,v); 64 } 65 rep(i,1,n) 66 if(!dfn[i])Tarjan(i); 67 //对图进行缩点,重新构图 68 rep(u,1,n) for(int i=head[u];~i;i=edge[i].next){ 69 int v=edge[i].to; 70 if(belong[u]!=belong[v]) 71 G[belong[u]].pb(belong[v]); 72 } 73 int ans=0; 74 rep(i,1,scc) 75 ans=max(ans,DFS(i)); //求缩点后图的最长路 76 printf("%d ",ans); 77 } 78 }
2018-11-28