UVa11324

来源:互联网 发布:天涯明月刀染色数据 编辑:程序博客网 时间:2024/06/08 00:31

题目链接

分析:
不强制双向到达,求最大团
显然一个强连通分量中的点要么都选,要么都不选
我们可以用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;   }
原创粉丝点击