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;
}