UVa11324 - The Largest Clique(Tarjan缩点+dp)

112 篇文章 0 订阅
64 篇文章 0 订阅

题目链接

分析:
不强制双向到达,求最大团
显然一个强连通分量中的点要么都选,要么都不选
我们可以用Tarjan把强连通分量缩点得到SCC图
SCC图中每一个点的权值就是这个SCC中点的数量
问题就转化成,
在图中选择若干个点使得权值和最大
求SCC图中的最大权路径

由于SCC图是一个DAG,我们可以用dp来解决这个问题
dp转移方程为f[x] = size[x] + max(f[y])

tip

第一次用vector存边,感觉挺好用的
但是由于原图是稠密图,所以还是用邻接表存储较好

TLE的原因是scc和pre数组一定要清零

//这里写代码片
#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#include<stack>
#include<algorithm>

using namespace std;

const int N=1002;
int n,m,scc[N],tot,low[N],pre[N],cnt,size[N];
int f[N],in[N],st[N],tt;
struct node{
    int y,nxt;
};
node way[50010];
vector<int> G[N];
stack<int> S;

void add(int u,int w)
{
    tt++;
    way[tt].y=w;way[tt].nxt=st[u];st[u]=tt;
}

void dfs(int now)
{
    pre[now]=low[now]=++tot;
    S.push(now);   //
    for (int i=st[now];i;i=way[i].nxt)
    {
        int v=way[i].y;
        if (!pre[v])
        {
            dfs(v);
            low[now]=min(low[now],low[v]);
        }
        else if (!scc[v])
        {
            low[now]=min(low[now],pre[v]);
        }
    }

    if (low[now]==pre[now])
    {
        cnt++;
        int tt=0;
        for (;;)
        {
            int x=S.top();
            S.pop();
            scc[x]=cnt;
            tt++;
            if (x==now) break;
        }
        size[cnt]=tt;   //SCC的大小 
    }
}

void find()
{
    memset(pre,0,sizeof(pre));
    memset(scc,0,sizeof(scc));
    while (!S.empty()) S.pop();
    tot=0; cnt=0;
    for (int i=1;i<=n;i++)
        if (!pre[i])
            dfs(i);
}

int dp(int now)
{
    if (f[now]) return f[now];   //记忆化
    int ans=0;
    for (int i=0;i<G[now].size();i++)
    {
        int v=G[now][i];
        ans=max(ans,dp(v));
    } 
    f[now]=ans+size[now];
    return f[now];
}

void solve()
{
    memset(in,0,sizeof(in));
    memset(f,0,sizeof(f));
    for (int i=1;i<=n;i++) G[i].clear(); 

    for (int i=1;i<=n;i++)
        for (int j=st[i];j;j=way[j].nxt)
        {
            int v=way[j].y;       //i--->v
            if (scc[i]!=scc[v])   //不在一个SCC中
            {
                G[scc[i]].push_back(scc[v]);
                in[scc[v]]++;
            } 
        }

    int ans=0;
    for (int i=1;i<=cnt;i++)
        if (!in[i])   //入度为0
           ans=max(ans,dp(i));
    printf("%d\n",ans);
}

int main()
{
    int T;
    scanf("%d",&T);
    while (T--)
    {
        memset(st,0,sizeof(st));
        tt=0;

        scanf("%d%d",&n,&m);
        for (int i=1;i<=m;i++)
        {
            int u,w;
            scanf("%d%d",&u,&w);
            add(u,w);
        }

        find();

        solve();
    }
    return 0;   
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值