Problem B: The Largest Clique
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 to v in G.
For each test case, output a single integer that is the size of the largest clique in T(G).
Sample input
1 5 5 1 2 2 3 3 1 4 1 5 2
Output for sample input
4
Zachary Friggstad
分析:
因为强连通分量内点相互可达,所以可看成一个点考虑,即缩点。缩点后,原图变为有向无环图(DAG),则答案为求DAG上的一条路径,使得该路径上点权和最大,
这里用bfs+拓扑顺序实现,这样可以避免重漏。另外,bfs时是重新建图的。
1A代码:
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<cmath> #include<vector> #include<cstdlib> #include<queue> #include<algorithm> #include<stack> using namespace std; #define LL long long #define ULL unsigned long long #define UINT unsigned int #define MAX_INT 0x7fffffff #define MAX_LL 0x7fffffffffffffff #define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) #define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) #define MAXN 1111 #define MAXM 111111 vector<int> g[MAXN], ng[MAXN]; stack<int> s; int dfn[MAXN], low[MAXN], in[MAXN]; int cnt, tsp, id[MAXN]; int num[MAXN], tn[MAXN]; bool m[MAXN][MAXN], ins[MAXN]; void tarjan(int u){ dfn[u]=low[u]=++tsp; s.push(u); ins[u]=true; int i, v, tl=g[u].size(); for(i=0; i<tl; i++){ v=g[u][i]; if(!dfn[v]) tarjan(v), low[u]=MIN(low[u], low[v]); else if(ins[v]) low[u]=MIN(low[u], dfn[v]); //ins避免横叉边 } if(low[u]==dfn[u]){ //将强连通分量缩为一点 cnt++; num[cnt]=0; // cout<<cnt<<endl; do{ v=s.top(); s.pop(); // cout<<' '<<v; id[v]=cnt; ins[v]=false; num[cnt]++; }while(v!=u); // cout<<" number:"<<num[cnt]<<endl; } } queue<int> q; int topsort(){ //DAG图,按拓扑序处理,求largest clique while(!q.empty()) q.pop(); memset(tn, 0, sizeof(tn)); for(int i=1; i<=cnt; i++) if(!in[i]) q.push(i); while(!q.empty()){ int u=q.front(); q.pop(); int i=0, tl=ng[u].size(); while(i<tl){ int v=ng[u][i]; //num表该点含多少缩点前的点,tn表由拓扑序祖先可加入的最大点量 tn[v]=MAX(tn[v], num[u]+tn[u]); in[v]--; //删边 if(!in[v]) q.push(v); //可作为当前拓扑序首项 i++; } } } int solve(int n){ tsp=cnt=0; memset(id, 0, sizeof(id)); memset(dfn, 0, sizeof(dfn)); memset(ins, 0, sizeof(ins)); for(int i=0; i<n; i++) if(!dfn[i]) //求强连通,缩点 tarjan(i); for(int i=1; i<=cnt; i++){ ng[i].clear(); } memset(m, 0, sizeof(m)); memset(in, 0, sizeof(in)); for(int i=0; i<n; i++){ //重新建图, int u=id[i], tl=g[i].size(); for(int j=0; j<tl; j++){ int v=id[g[i][j]]; if(u!=v && !m[u][v]){ //避免重边 in[v]++; //计算入度 ng[u].push_back(v); m[u][v]=true; } } } topsort(); int ans=0; for(int i=1; i<=cnt; i++){ ans=MAX(num[i]+tn[i], ans); } return ans; } int main(){ //freopen("C:\Users\Administrator\Desktop\in.txt","r",stdin); int n, m, T; scanf(" %d",&T); while(T--){ scanf(" %d %d", &n, &m); int i, u, v; for(i=0; i<n; i++) g[i].clear(); for(i=0; i<m; i++){ scanf(" %d %d", &u, &v); u--; v--; g[u].push_back(v); } int ans=solve(n); printf("%d ", ans); } return 0; }