http://vjudge.net/contest/view.action?cid=48251#problem/E
Given a directed graph G, consider the following transformation. First, create a new graph T(G) to have the same vertex set as G. Create a directed edge between two vertices u and v in T(G) if and only if there is a path between u and v in G that follows the directed edges only in the forward direction. This graph T(G) is often called the transitive closure of G.
We define a clique in a directed graph as a set of vertices U such that for any two vertices u and v in U, there is a directed edge either from u to v or from v to u (or both). The size of a clique is the number of vertices in the clique.
The number of cases is given on the first line of input. Each test case describes a graph G. It begins with a line of two integers n and m, where 0 ≤ n ≤ 1000 is the number of vertices of G and 0 ≤ m ≤ 50,000 is the number of directed edges of G. The vertices of G are numbered from 1 to n. The following m lines contain two distinct integers u and v between 1 and n which define a directed edge from u tov in G.
For each test case, output a single integer that is the size of the largest clique in T(G).
1 5 5 1 2 2 3 3 1 4 1 5 2
4题目大意:给一张有向图G,求一个节点数最大的节点集,使得该点集中,任意两个节点u和v满足:要么u可以到达v,要么v可以到达u(u和v相互到达也可以);
解题思路:不难发现在最优的方案中,同一个强连通分量中的点要么都选,要么都不选。把强连通分量搜索点后得到一个无环图,每一个节点的权等于它的节点数,则题目转化为求该无环图上权最大的路径。可用动态规划求解。
#include <stdio.h> #include <string.h> #include <iostream> #include <stack> using namespace std; const int N=1002; const int M=50005; struct note { int u; int v; int next; } edge[M],a[M]; int head[N],ip,in[N],Q[N],dfn[N],low[N],sccno[N],dp[N]; bool _map[N][N]; int n,m,stamp,cnt_edge,scc_cnt,top,scc_val[N]; stack <int> q; void addedge(int u,int v) { edge[ip].v=v; edge[ip].next=head[u]; head[u]=ip++; } void init() { scc_cnt=0; stamp=0; memset(in,0,sizeof(in)); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(scc_val,0,sizeof(scc_val)); memset(_map,0,sizeof(_map)); memset(dp,0,sizeof(dp)); memset(sccno,0,sizeof(sccno)); memset(head,-1,sizeof(head)); ip=0; } void tarjan(int u) { dfn[u]=low[u]=++stamp; q.push(u); for(int i=head[u]; i!=-1; i=edge[i].next) { int v=edge[i].v; if(!dfn[v]) { tarjan(v); low[u]=min(low[u],low[v]); } else if(!scc_val[v]) low[u]=min(low[u],dfn[v]); } if(dfn[u]==low[u]) { scc_cnt++; int x; do { x=q.top(); q.pop(); scc_val[x]=scc_cnt; sccno[scc_cnt]++; } while(x!=u); } } void DP() { int car=0,cdr=0; /*for(int i=1;i<=scc_cnt;i++) printf("(%d %d)\n",in[i],sccno[i]);*/ for(int i=1; i<=scc_cnt; i++) { if(in[i]==0) Q[car++]=i,dp[i]=sccno[i]; } while(car!=cdr) { int u=Q[cdr++]; for(int k=head[u]; k!=-1; k=edge[k].next) { int v=edge[k].v; dp[v]=max(dp[v],dp[u]+sccno[v]); if(--in[v]==0) Q[car++]=v; } } } int main() { int T,u,v; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); init(); for(int i=0; i<m; i++) { scanf("%d%d",&u,&v); addedge(u,v); a[i].u=u,a[i].v=v; } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); memset(head,-1,sizeof(head)); ip=0; for(int i=0; i<m; i++) { u=a[i].u,v=a[i].v; if(scc_val[u]!=scc_val[v]&&!_map[scc_val[u]][scc_val[v]]) { addedge(scc_val[u],scc_val[v]); in[scc_val[v]]++; _map[scc_val[u]][scc_val[v]]=true; } } DP(); int ans = 0; for (int i = 1; i <= scc_cnt; i++) ans = max(ans, dp[i]); printf("%d\n", ans); } return 0; }