UVa11324

题目链接

分析:
不强制双向到达,求最大团
显然一个强连通分量中的点要么都选,要么都不选
我们可以用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
",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;   
}
原文地址:https://www.cnblogs.com/wutongtong3117/p/7673011.html