• Tarjan缩点割点(模板)


    描述:https://www.luogu.com.cn/problem/P3387

    给定一个 nn 个点 mm 条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。

    允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次.


    #include <iostream>
    #include <vector>
    #include <queue>
    using namespace std;
    const int maxn=10009;
    int n,m,p[maxn],dp[maxn];
    struct edge{
        int u,to,nxt;
    }d[maxn*10];int head[maxn*10],cnt=1;
    void add(int u,int v){
        d[cnt].u=u,d[cnt].to=v,d[cnt].nxt=head[u],head[u]=cnt++;
    }
    int dfn[maxn],low[maxn],id,stack[maxn],vis[maxn],top,sd[maxn];//为tarjan准备 
    void tarjan(int now)
    {
        dfn[now]=low[now]=++id;
        stack[++top]=now,vis[now]=1;
        for(int i=head[now];i;i=d[i].nxt)
        {
            int w=d[i].to;
            if(!dfn[w])    
                tarjan(w),low[now]=min(low[now],low[w]);
            else if(vis[w])
                low[now]=min(low[now],low[w]);    
        }
        if(low[now]==dfn[now])
        {
            int temp;
            while(temp=stack[top--])
            {
                sd[temp]=now;
                vis[temp]=0;
                if(temp==now)    break;
                p[now]+=p[temp];//集中在now这个超级点上 
            }
        }
    } 
    int indug[maxn];
    vector<int>vec[maxn];
    int tuopu()
    {
        queue<int>q;
        for(int i=1;i<=n;i++)    if(!indug[i]&&sd[i]==i)    q.push(i),dp[i]=p[i];
        while(!q.empty())
        {
            int now=q.front();q.pop();
            for(int i=0;i<vec[now].size();i++)
            {
                int w=vec[now][i];
                dp[w]=max(dp[w],dp[now]+p[w]);
                if(--indug[w]==0)    q.push(w);
            }
        }
        int ans=0;
        for(int i=1;i<=n;i++)    ans=max(ans,dp[i]);
        return ans;
    } 
    int main()
    {
        cin>>n>>m;
        for(int i=1;i<=n;i++)    cin>>p[i];//读入点权
        for(int i=1;i<=m;i++)
        {
            int l,r;cin>>l>>r;
            add(l,r);    
        }
        for(int i=1;i<=n;i++)
        if(!dfn[i])    tarjan(i);
        for(int i=1;i<=m;i++)
        {
            int x=sd[d[i].u],y=sd[d[i].to];//看看两头是否是连通分量
            if(x!=y)//不是就建边 
                vec[x].push_back(y),indug[y]++;
        }
        cout<<tuopu();
        return 0;
    }

     还有割点的

    为什么(“low[v]>=dfn[u],此时u就是割点”)??

    因为后面的点无法回到u点之前

    u就把两个部分分开来了

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=200009;
    struct edge{
        int nxt,to;
    }d[maxn];
    int n,m,id,cnt=1,ttp;
    int head[maxn],dfn[maxn],low[maxn],cut[maxn];
    void add(int u,int v){
        d[cnt].to=v,d[cnt].nxt=head[u],head[u]=cnt++;
    }
    void tarjan(int u,int fa)
    {
        dfn[u]=low[u]=++id;
        int child=0;
        for(int i=head[u];i;i=d[i].nxt)
        {
            int w=d[i].to;
            if(!dfn[w])
            {
                tarjan(w,fa);
                low[u]=min(low[u],low[w]);
                if(low[w]>=dfn[u]&&u!=fa)//通过非非节点更新的low[w]
                //因为在本连通块能回溯最多到dfn[u] 
                    cut[u]=1;
                if(u==fa)    child++;
            }
            low[u]=min(low[u],dfn[w]);
        }
        if(child>=2&&u==fa)    cut[u]=1;
    }
    int main()
    {
        cin>>n>>m;
        for(int i=1;i<=m;i++)
        {
            int l,r;
            cin>>l>>r;
            add(l,r);add(r,l);
        }
        for(int i=1;i<=n;i++)
        if(!dfn[i])    tarjan(i,i);
        int ans=0;
        for(int i=1;i<=n;i++)
            if(cut[i])    ans++;
        cout<<ans<<endl;
        for(int i=1;i<=n;i++)
            if(cut[i])
                cout<<i<<" ";
        return 0; 
    }
    View Code
  • 相关阅读:
    (转)回车 执行button点击
    (转)sp_executesql介绍和使用
    (转)SQL Server 2008将数据导出为脚本 [SQL Server]
    (转)Phonegap VS AppCan
    (转)asp.net动态设置标题title 关键字keywords 描述descrtptions
    Ubuntu Tomcat Service
    Ubuntu使用ssh方法连接不上
    Ubuntu输入su提示认证失败的解决方法
    Redis通过命令行进行配置
    linux下安装redis3.2
  • 原文地址:https://www.cnblogs.com/iss-ue/p/12524937.html
Copyright © 2020-2023  润新知