UVA 11324 The Largest Clique [强连通分量] [拓扑排序&dp]

38 篇文章 1 订阅
9 篇文章 0 订阅
该博客介绍了如何解决图论问题中的寻找最大点集任务,涉及概念包括图的强连通分量(Tarjan算法)、拓扑排序及动态规划(DAG)。给定一个有向图G,目标是找到最大的顶点集合,其中任意两点间存在双向路径。文章通过实例解析了如何利用这些技术找到图T(G)的最大 clique。
摘要由CSDN通过智能技术生成

The Largest Clique
Time Limit: 3000MS 64bit IO Format: %lld & %llu

Description
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.

Input
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.

Output
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

Sample Output
4


求最大点集使得集合中任意两个点u v 满足 u能到v || v能到u 。。。
那么在同一个SCC中的点要么全取(对答案贡献更大)要么全部不去,那么缩点之后拓扑排序进行dp就可以了。。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<climits>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=2005;
struct Edge
{
    int to,next;
}edge[maxn*maxn];
int head[maxn];
int maxedge;
inline void addedge(int u,int v)
{
    edge[++maxedge]=(Edge){v,head[u]};
    head[u]=maxedge;
}
int n,m;
void init()
{
    scanf("%d%d",&n,&m);
    memset(head,-1,sizeof(head));
    maxedge=-1;
    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        addedge(x,y);
    }
}
int dfs_clock;
int dfn[maxn],sccno[maxn];
bool insta[maxn];
stack <int> sta;
struct Scc
{
    int cnt;
    vector <int> a[maxn];
    void clear()
    {
        cnt=0;
        for(int i=1;i<=n+1;i++) a[i].clear();
    }
    void add(int tmp) { a[cnt].push_back(tmp); sccno[tmp]=cnt; }
}scc;
int dfs(int u)
{
    int lowu=dfn[u]=++dfs_clock;
    sta.push(u);insta[u]=true;
    for(int i=head[u];~i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(!dfn[v])
        {
            int lowv=dfs(v);
            lowu=min(lowu,lowv);
        }
        else if(insta[v]) lowu=min(lowu,dfn[v]);
    }
    if(lowu>=dfn[u])
    {
        scc.cnt++;
        int tmp;
        do
        {
            tmp=sta.top();sta.pop();insta[tmp]=false;
            scc.add(tmp);
        }while(tmp^u);
    }
    return lowu;
}
void tarjan()
{
    memset(dfn,0,sizeof(dfn));
    memset(sccno,0,sizeof(sccno));
    dfs_clock=0;scc.clear();//!!!!!significant to clear the scc!!!!!!
    for(int i=1;i<=n;i++)
        if(!dfn[i]) dfs(i);
}
Edge edge2[maxn*maxn];
int head2[maxn];
int maxedge2;
inline void addedge2(int u,int v)
{
    edge2[++maxedge2]=(Edge){v,head2[u]};
    head2[u]=maxedge2;
}
int in[maxn],val[maxn];//,out[maxn]
stack <int> topo;
int topos[maxn],dp[maxn];
void toposort()
{
    for(int k=1;k<=scc.cnt;k++)
        if(!in[k]) topo.push(k);
    int pos=0;//up to scc.cnt
    while(!topo.empty())
    {
        int tmp=topo.top();topo.pop();
        topos[++pos]=tmp;
        for(int i=head2[tmp];~i;i=edge2[i].next)
        {
            int v=edge2[i].to;
            if(--in[v]==0) topo.push(v);
        }
    }
}
int dynamic()
{
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=scc.cnt;i++) dp[topos[i]]=val[topos[i]];//last WA: dp[i]=val[yopos[i]]
    for(int i=1;i<=scc.cnt;i++)//i for sub in dp, dp indicates the sccno
    {
        for(int j=head2[topos[i]];~j;j=edge2[j].next)
        {
            int v=edge2[j].to;//the sccno
            dp[v]=max(dp[v],dp[topos[i]]+val[v]);//last WA: dp[i] && dp[topos[v]]!!
        }
    }
    return *max_element(dp+1,dp+scc.cnt+1);
}
void work()
{
    tarjan();
    memset(head2,-1,sizeof(head2));
    memset(in,0,sizeof(in));
    //memset(out,0,sizeof(out));
    maxedge2=-1;
    for(int i=1;i<=n;i++)
        for(int j=head[i];~j;j=edge[j].next)
            if(sccno[i]^sccno[edge[j].to]) in[sccno[edge[j].to]]++,addedge2(sccno[i],sccno[edge[j].to]);//,out[sccno[i]]++
    for(int k=1;k<=scc.cnt;k++)
        val[k]=scc.a[k].size();
    toposort();
    printf("%d\n",dynamic());
}
int main()
{
    #ifdef Local
    freopen("clique.in","r",stdin);
    freopen("clique.out","w",stdout);
    #endif
    int cas;
    scanf("%d",&cas);
    while(cas--)
    {
        init();
        work();
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值