zoukankan      html  css  js  c++  java
  • 算法笔记--强连通分量分解

    Kosaraju算法

    详见《挑战程序设计竞赛》p320

    模板:

    const int N=1e5+5;
    int n,m;
    vector<int>g[N];
    vector<int>rg[N];
    vector<int>vs;
    bool vis[N];
    int cmp[N];
    
    void add_edge(int u,int v)
    {
        g[u].pb(v);
        rg[v].pb(u);
    } 
    
    void dfs(int u)
    {
        vis[u]=true;
        for(int i=0;i<g[u].size();i++)
        if(!vis[g[u][i]])dfs(g[u][i]);
        vs.pb(u);
    }
    
    void rdfs(int u,int k)
    {
        vis[u]=true;
        cmp[u]=k;
        for(int i=0;i<rg[u].size();i++)
        if(!vis[rg[u][i]])rdfs(rg[u][i],k);
    }
    
    int scc()
    {
        mem(vis,false);
        vs.clear();
        for(int i=1;i<=n;i++)if(!vis[i])dfs(i);
        
        mem(vis,false);
        int k=0;
        for(int i=vs.size()-1;i>=0;i--)if(!vis[vs[i]])rdfs(vs[i],k++);
        return k; 
    }

    Tarjan算法模板: 

    const int N = 1e5 + 5;
    vector<int> g[N];
    int low[N], dfn[N], stk[N], cmp[N], cnt = 0, top = 0, tot = 0;
    bool vis[N];
    void tarjan(int u) {
        low[u] = dfn[u] = ++cnt;
        stk[++top] = u;
        vis[u] = true; //标记是否在栈中
        for (int v : g[u]) {
            if(!dfn[v]) {
                tarjan(v);
                low[u] = min(low[u], low[v]);
            }
            else if(vis[v]) low[u] =  min(low[u], dfn[v]);
        }
        if(low[u] == dfn[u]) {
            cmp[u] = ++tot;
            vis[u] = false;
            while(stk[top] != u) {
                cmp[stk[top]] = tot;
                vis[stk[top--]] = false;
            }
            top--;
        }
    }

     例题1:POJ 2186 Popular Cows

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<vector>
    using namespace std;
    #define ll long long
    #define pb push_back
    #define mem(a,b) memset(a,b,sizeof(a))
    const int N=1e5+5;
    int n,m;
    vector<int>g[N];
    vector<int>rg[N];
    vector<int>vs;
    bool vis[N];
    int cmp[N];
    
    void add_edge(int u,int v)
    {
        g[u].pb(v);
        rg[v].pb(u);
    } 
    
    void dfs(int u)
    {
        vis[u]=true;
        for(int i=0;i<g[u].size();i++)
        if(!vis[g[u][i]])dfs(g[u][i]);
        vs.pb(u);
    }
    
    void rdfs(int u,int k)
    {
        vis[u]=true;
        cmp[u]=k;
        for(int i=0;i<rg[u].size();i++)
        if(!vis[rg[u][i]])rdfs(rg[u][i],k);
    }
    
    int scc()
    {
        mem(vis,false);
        vs.clear();
        for(int i=1;i<=n;i++)if(!vis[i])dfs(i);
        
        mem(vis,false);
        int k=0;
        for(int i=vs.size()-1;i>=0;i--)if(!vis[vs[i]])rdfs(vs[i],k++);
        return k; 
    }
    
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
        cin>>n>>m;
        int u,v;
        for(int i=0;i<m;i++)cin>>u>>v,add_edge(u,v);
        
        int t=scc();
        int ans=0;
        for(int i=1;i<=n;i++)if(cmp[i]==t-1)ans++,u=i;
        
        mem(vis,false);
        rdfs(u,0);
        
        for(int i=1;i<=n;i++)if(!vis[i])ans=0;
        cout<<ans<<endl; 
        return 0;
    } 
    View Code

    例题2:POJ 1236 Network of Schools

    思路:统计出入度,第一个答案输出强连通分量入度为0的个数,第二个答案输出max(入度为0个数,出度为0的个数),如果只有一个强连通,就是0。

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    using namespace std;
    #define ll long long
    #define pb push_back
    #define mem(a,b) memset(a,b,sizeof(a))
    
    const int N=105;
    vector<int>g[N];
    vector<int>rg[N];
    vector<int>vs;
    bool vis[N];
    int cmp[N];
    int in[N]={0};
    int out[N]={0};
    int n,a;
    
    void add_edge(int u,int v)
    {
        g[u].pb(v);
        rg[v].pb(u);
    }
    
    void dfs(int u)
    {
        vis[u]=true;
        for(int i=0;i<g[u].size();i++)if(!vis[g[u][i]])dfs(g[u][i]);
        vs.pb(u);
    }
    
    void rdfs(int u,int k)
    {
        vis[u]=true;
        cmp[u]=k;
        for(int i=0;i<rg[u].size();i++)if(!vis[rg[u][i]])rdfs(rg[u][i],k);
    }
    
    int scc()
    {
        mem(vis,false);
        vs.clear();
        for(int i=1;i<=n;i++)if(!vis[i])dfs(i);
        
        mem(vis,false);
        int k=0;
        for(int i=vs.size()-1;i>=0;i--)if(!vis[vs[i]])rdfs(vs[i],k++);
        return k;
    }
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
        
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            while(cin>>a&&a)add_edge(i,a);
        }
        
        int t=scc();
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<g[i].size();j++)
            if(cmp[i]!=cmp[g[i][j]])out[cmp[i]]++,in[cmp[g[i][j]]]++;
        }
        int in1=0,out1=0;
        for(int i=0;i<t;i++)
        {
            //cout<<in[i]<<' '<<out[i]<<endl;
            if(in[i]==0)in1++;
            if(out[i]==0)out1++;
        }
        
        cout<<in1<<endl;
        if(t==1)cout<<0<<endl;
        else cout<<max(in1,out1)<<endl;
        return 0;
    } 
    View Code

    例题3:POJ 1904 King's Quest

    思路:能结婚的两个人肯定在一个强连通分量里。

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<vector> 
    using namespace std;
    #define ll long long
    #define pb push_back
    #define mem(a,b) memset(a,b,sizeof(a))
    
    const int N=4e3+5;
    vector<int>g[N];
    vector<int>rg[N];
    vector<int>vs;
    vector<int>ans;
    bool vis[N];
    bool mp[N/2][N/2];
    int cmp[N];
    int n,k,a;
    
    void add_edge(int u,int v)
    {
        g[u].pb(v);
        rg[v].pb(u);
    }
    
    void dfs(int u)
    {
        vis[u]=true;
        for(int i=0;i<g[u].size();i++)if(!vis[g[u][i]])dfs(g[u][i]);
        vs.pb(u);
    }
    
    void rdfs(int u,int k)
    {
        vis[u]=true;
        cmp[u]=k;
        for(int i=0;i<rg[u].size();i++)if(!vis[rg[u][i]])rdfs(rg[u][i],k);
    }
    
    int scc()
    {
        mem(vis,false);
        vs.clear();
        for(int i=1;i<=2*n;i++)if(!vis[i])dfs(i);
        
        int k=0;
        mem(vis,false);
        for(int i=vs.size()-1;i>=0;i--)if(!vis[vs[i]])rdfs(vs[i],k++);
        return k;
    }
    
    int main()
    {
        /*ios::sync_with_stdio(false);
        cin.tie(0);*/
        
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&k);
            while(k--)
            {
                scanf("%d",&a);
                add_edge(i,n+a);
                mp[i][a]=true;
            }
        }
        for(int i=1;i<=n;i++)scanf("%d",&a),add_edge(n+a,i); 
        int t=scc();
        //cout<<t<<endl;
        for(int i=1;i<=n;i++)
        {
            ans.clear();
            for(int j=1;j<=n;j++)
            if(cmp[j+n]==cmp[i]&&mp[i][j])ans.pb(j);
            printf("%d ",ans.size());
            for(int j=0;j<ans.size()-1;j++)printf("%d ",ans[j]);
            printf("%d
    ",ans[ans.size()-1]);
        }
        return 0;
    } 
    View Code

    例题4:HDU 1269 迷宫城堡

    坑点:当n不为0时,m为0不需要退出输入,因为此时是个非联通图。

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<vector> 
    using namespace std;
    #define ll long long
    #define pb push_back
    #define mem(a,b) memset(a,b,sizeof(a))
    
    const int N=1e4+5;
    vector<int>g[N];
    vector<int>rg[N];
    vector<int>vs;
    bool vis[N];
    int cmp[N];
    int n,m,u,v;
    
    void add_edge(int u,int v)
    {
        g[u].pb(v);
        rg[v].pb(u);
    }
    
    void dfs(int u)
    {
        vis[u]=true;
        for(int i=0;i<g[u].size();i++)if(!vis[g[u][i]])dfs(g[u][i]);
        vs.pb(u);
    }
    
    void rdfs(int u,int k)
    {
        vis[u]=true;
        cmp[u]=k;
        for(int i=0;i<rg[u].size();i++)if(!vis[rg[u][i]])rdfs(rg[u][i],k);
    }
    
    int scc()
    {
        mem(vis,false);
        vs.clear();
        for(int i=1;i<=n;i++)if(!vis[i])dfs(i);
        
        int k=0;
        mem(vis,false);
        for(int i=vs.size()-1;i>=0;i--)if(!vis[vs[i]])rdfs(vs[i],k++);
        return k;
    }
    
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
        
        while(cin>>n>>m&&(n||m))
        {
            for(int i=1;i<=n;i++)g[i].clear(),rg[i].clear();
            while(m--)
            {
                cin>>u>>v;
                add_edge(u,v);
            }
            int t=scc();
            //cout<<t<<endl;
            if(t==1)cout<<"Yes"<<endl;
            else cout<<"No"<<endl;
        }
        return 0;
    } 
    View Code

    例题5:HDU 2767 Proving Equivalences

    思路:见例题2。

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<vector> 
    using namespace std;
    #define ll long long
    #define pb push_back
    #define mem(a,b) memset(a,b,sizeof(a))
    
    const int N=2e4+5;
    vector<int>g[N];
    vector<int>rg[N];
    vector<int>vs;
    bool vis[N];
    int cmp[N];
    int in[N];
    int out[N];
    int n,m,u,v;
    
    void add_edge(int u,int v)
    {
        g[u].pb(v);
        rg[v].pb(u);
    }
    
    void dfs(int u)
    {
        vis[u]=true;
        for(int i=0;i<g[u].size();i++)if(!vis[g[u][i]])dfs(g[u][i]);
        vs.pb(u);
    }
    
    void rdfs(int u,int k)
    {
        vis[u]=true;
        cmp[u]=k;
        for(int i=0;i<rg[u].size();i++)if(!vis[rg[u][i]])rdfs(rg[u][i],k);
    }
    
    int scc()
    {
        mem(vis,false);
        vs.clear();
        for(int i=1;i<=n;i++)if(!vis[i])dfs(i);
        
        int k=0;
        mem(vis,false);
        for(int i=vs.size()-1;i>=0;i--)if(!vis[vs[i]])rdfs(vs[i],k++);
        return k;
    }
    
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
        int t;
        cin>>t;
        while(t--)
        {
            cin>>n>>m;
            for(int i=1;i<=n;i++)g[i].clear(),rg[i].clear();
            mem(in,0);
            mem(out,0);
            while(m--)
            {
                cin>>u>>v;
                add_edge(u,v);
            }
            int t=scc();
            for(int i=1;i<=n;i++)
            {
                for(int j=0;j<g[i].size();j++)
                {
                    if(cmp[i]!=cmp[g[i][j]])
                    out[cmp[i]]++,in[cmp[g[i][j]]]++;
                }
            }
            int _in=0,_out=0;
            for(int i=0;i<t;i++)
            {
                if(in[i]==0)_in++;
                if(out[i]==0)_out++;
            }
            if(t==1)cout<<0<<endl;
            else cout<<max(_in,_out)<<endl;
        }
        return 0;
    } 
    View Code

    例题6:HDU 1827 Summer Holiday

    思路:计算一下联系每一个强连通的最小话费。

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<vector> 
    using namespace std;
    #define ll long long
    #define pb push_back
    #define mem(a,b) memset(a,b,sizeof(a))
    
    const int N=2e4+5;
    const int INF=0x3f3f3f3f;
    vector<int>g[N];
    vector<int>rg[N];
    vector<int>vs;
    bool vis[N];
    int cmp[N];
    int in[N];
    int out[N];
    int mn[N];
    int cost[N];
    int n,m,u,v;
    
    void add_edge(int u,int v)
    {
        g[u].pb(v);
        rg[v].pb(u);
    }
    
    void dfs(int u)
    {
        vis[u]=true;
        for(int i=0;i<g[u].size();i++)if(!vis[g[u][i]])dfs(g[u][i]);
        vs.pb(u);
    }
    
    void rdfs(int u,int k)
    {
        vis[u]=true;
        cmp[u]=k;
        for(int i=0;i<rg[u].size();i++)if(!vis[rg[u][i]])rdfs(rg[u][i],k);
    }
    
    int scc()
    {
        mem(vis,false);
        vs.clear();
        for(int i=1;i<=n;i++)if(!vis[i])dfs(i);
        
        int k=0;
        mem(vis,false);
        for(int i=vs.size()-1;i>=0;i--)if(!vis[vs[i]])rdfs(vs[i],k++);
        return k;
    }
    
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
        while(cin>>n>>m)
        {
            for(int i=1;i<=n;i++)cin>>cost[i];
            for(int i=1;i<=n;i++)g[i].clear(),rg[i].clear();
            mem(in,0);
            mem(out,0);
            mem(mn,INF);
            while(m--)
            {
                cin>>u>>v;
                add_edge(u,v);
            }
            int t=scc();
            for(int i=1;i<=n;i++)
            {
                for(int j=0;j<g[i].size();j++)
                {
                    if(cmp[i]!=cmp[g[i][j]])
                    out[cmp[i]]++,in[cmp[g[i][j]]]++;
                }
                 mn[cmp[i]]=min(mn[cmp[i]],cost[i]);
            }
            
            ll ans=0;
            int cnt=0;
            for(int i=0;i<t;i++)
            {
                if(in[i]==0)ans+=mn[i],cnt++;
            }
            cout<<cnt<<' '<<ans<<endl;
        }
        return 0;
    } 
    View Code

    例题7:POJ 2553 The Bottom of Graph

    思路:把英文读懂了就明白怎么做了:http://poj.org/showmessage?message_id=162296,其实就是找所有出度为0的强连通分支

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<vector> 
    using namespace std;
    #define ll long long
    #define pb push_back
    #define mem(a,b) memset(a,b,sizeof(a))
    
    const int N=2e4+5;
    const int INF=0x3f3f3f3f;
    vector<int>g[N];
    vector<int>rg[N];
    vector<int>vs;
    vector<int>ans;
    bool vis[N];
    int cmp[N];
    int in[N];
    int out[N];
    int n,m,u,v;
    
    void add_edge(int u,int v)
    {
        g[u].pb(v);
        rg[v].pb(u);
    }
    
    void dfs(int u)
    {
        vis[u]=true;
        for(int i=0;i<g[u].size();i++)if(!vis[g[u][i]])dfs(g[u][i]);
        vs.pb(u);
    }
    
    void rdfs(int u,int k)
    {
        vis[u]=true;
        cmp[u]=k;
        for(int i=0;i<rg[u].size();i++)if(!vis[rg[u][i]])rdfs(rg[u][i],k);
    }
    
    int scc()
    {
        mem(vis,false);
        vs.clear();
        for(int i=1;i<=n;i++)if(!vis[i])dfs(i);
        
        int k=0;
        mem(vis,false);
        for(int i=vs.size()-1;i>=0;i--)if(!vis[vs[i]])rdfs(vs[i],k++);
        return k;
    }
    
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
        while(cin>>n&&n&&cin>>m)
        {
            for(int i=1;i<=n;i++)g[i].clear(),rg[i].clear();
            mem(in,0);
            mem(out,0);
            for(int i=0;i<m;i++)
            {
                cin>>u>>v;
                add_edge(u,v);
            }
            int t=scc();
            ans.clear(); 
            for(int i=1;i<=n;i++)
            {
                for(int j=0;j<g[i].size();j++)
                if(cmp[i]!=cmp[g[i][j]])
                {
                    out[cmp[i]]++;
                    in[cmp[g[i][j]]]++;
                }
            } 
            for(int i=1;i<=n;i++)
            {
                if(out[cmp[i]]==0)ans.pb(i);
             } 
             for(int i=0;i<ans.size();i++)
             {
                 cout<<ans[i];
                 if(i!=ans.size()-1)cout<<' ';
             }
            cout<<endl;
        }
        return 0;
    } 
    View Code

    例题8:POJ 2762 Going from u to v or from v to u?

    思路:强连通缩点+拓扑排序,顺便学了一波队列求拓扑排序http://blog.csdn.net/lisonglisonglisong/article/details/45543451

    删边后入度为0的点不能超过1个,详见:http://www.cnblogs.com/scau20110726/archive/2013/05/23/3094495.html

    代码1(邻接矩阵保存缩点后的DAG):

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<vector>
    #include<queue>
    using namespace std;
    #define ll long long
    #define pb push_back
    #define mem(a,b) memset(a,b,sizeof(a))
    
    const int N=1e3+5;
    vector<int>g[N];
    vector<int>rg[N]; 
    vector<int>vs;
    bool vis[N];
    int cmp[N];
    int in[N];
    int out[N];
    bool newg[N][N];
    int n,m,u,v;
    
    void add_edge(int u,int v)
    {
        g[u].pb(v);
        rg[v].pb(u);    
    }
    
    void dfs(int u)
    {
        vis[u]=true; 
        for(int i=0;i<g[u].size();i++)if(!vis[g[u][i]])dfs(g[u][i]);
        vs.pb(u);
    }
    
    void rdfs(int u,int k)
    {
        vis[u]=true;
        cmp[u]=k;
        for(int i=0;i<rg[u].size();i++)if(!vis[rg[u][i]])rdfs(rg[u][i],k);
    }
    
    int scc()
    {
        mem(vis,false);
        vs.clear();
        for(int i=1;i<=n;i++)if(!vis[i])dfs(i);
        
        int k=0;
        mem(vis,false);
        for(int i=vs.size()-1;i>=0;i--)if(!vis[vs[i]])rdfs(vs[i],k++);
        return k;
    }
    
    void init()
    {
        for(int i=1;i<=n;i++)g[i].clear(),rg[i].clear();
        mem(newg,false);
        mem(in,0);
        mem(out,0);
    }
    
    bool topo_sort(int t)
    {
        queue<int>q;
        int cnt=0;
        for(int i=0;i<t;i++)if(in[i]==0)cnt++,q.push(i);
        if(cnt>1)return false;
        while(!q.empty())
        {
            cnt=0;
            int u=q.front();
            q.pop();
            for(int i=0;i<t;i++)
            {
                if(newg[u][i])
                {
                    in[i]--;
                    if(in[i]==0)q.push(i),cnt++;
                }
            }
            if(cnt>1)return false;
        }
        return true;
    }
    
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
        int t;
        cin>>t;
        while(t--)
        {
            cin>>n>>m;
            init();
            for(int i=0;i<m;i++)
            {
                cin>>u>>v;
                add_edge(u,v);
            }
            int tt=scc();
            for(int i=1;i<=n;i++)
            {
                for(int j=0;j<g[i].size();j++)
                {
                    if(cmp[i]!=cmp[g[i][j]])newg[cmp[i]][cmp[g[i][j]]]=true;
                } 
            } 
            for(int i=0;i<tt;i++)
            {
                for(int j=0;j<tt;j++)
                if(newg[i][j])out[i]++,in[j]++;
            }
            if(topo_sort(tt))cout<<"Yes"<<endl;
            else cout<<"No"<<endl;
        }
        return 0;
    } 
    View Code

    代码2(邻接表保存缩点后的DAG,有重边)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<vector>
    #include<queue>
    using namespace std;
    #define ll long long
    #define pb push_back
    #define mem(a,b) memset(a,b,sizeof(a))
    
    const int N=1e3+5;
    vector<int>g[N];
    vector<int>rg[N]; 
    vector<int>newg[N];
    vector<int>vs;
    bool vis[N];
    int cmp[N];
    int in[N];
    int out[N];
    int n,m,u,v;
    
    void add_edge(int u,int v)
    {
        g[u].pb(v);
        rg[v].pb(u);    
    }
    
    void dfs(int u)
    {
        vis[u]=true; 
        for(int i=0;i<g[u].size();i++)if(!vis[g[u][i]])dfs(g[u][i]);
        vs.pb(u);
    }
    
    void rdfs(int u,int k)
    {
        vis[u]=true;
        cmp[u]=k;
        for(int i=0;i<rg[u].size();i++)if(!vis[rg[u][i]])rdfs(rg[u][i],k);
    }
    
    int scc()
    {
        mem(vis,false);
        vs.clear();
        for(int i=1;i<=n;i++)if(!vis[i])dfs(i);
        
        int k=0;
        mem(vis,false);
        for(int i=vs.size()-1;i>=0;i--)if(!vis[vs[i]])rdfs(vs[i],k++);
        return k;
    }
    
    void init()
    {
        for(int i=0;i<=n;i++)g[i].clear(),rg[i].clear(),newg[i].clear();
        mem(in,0);
        mem(out,0);
    }
    
    bool topo_sort(int t)
    {
        queue<int>q;
        int cnt=0;
        for(int i=0;i<t;i++)if(in[i]==0)cnt++,q.push(i);
        if(cnt>1)return false;
        while(!q.empty())
        {
            cnt=0;
            int u=q.front();
            q.pop();
            for(int i=0;i<newg[u].size();i++)
            {
                int v=newg[u][i];
                in[v]--;
                if(in[v]==0)cnt++,q.push(v);
            }
            if(cnt>1)return false;
        }
        return true;
    }
    
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
        int t;
        cin>>t;
        while(t--)
        {
            cin>>n>>m;
            init();
            for(int i=0;i<m;i++)
            {
                cin>>u>>v;
                add_edge(u,v);
            }
            int tt=scc();
            for(int i=1;i<=n;i++)
            {
                for(int j=0;j<g[i].size();j++)
                {
                    if(cmp[i]!=cmp[g[i][j]])
                    {
                        newg[cmp[i]].pb(cmp[g[i][j]]);
                        out[cmp[i]]++;
                        in[cmp[g[i][j]]]++;
                    }
                } 
            } 
            
            if(topo_sort(tt))cout<<"Yes"<<endl;
            else cout<<"No"<<endl;
        }
        return 0;
    } 
    View Code

    例题9:POJ 3169 Father Christmas flymouse

    思路:强连通缩点成DAG,对于每一个入度为0的节点,dfs求出最大的ans。

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<vector>
    using namespace std;
    #define ll long long
    #define pb push_back
    #define mem(a,b) memset(a,b,sizeof(a))
    
    const int N=3e4+5;
    vector<int>g[N];
    vector<int>rg[N];
    vector<int>ng[N];
    vector<int>vs;
    bool vis[N];
    int a[N];
    int in[N];
    int cmp[N];
    int value[N];
    int n,m,u,v;
    
    void init()
    {
        for(int i=0;i<=n;i++)g[i].clear(),rg[i].clear(),ng[i].clear();
        mem(in,0);
        mem(value,0);
    }
    
    void add_edge(int u,int v)
    {
        g[u].pb(v);
        rg[v].pb(u);
    }
    
    void dfs(int u)
    {
        vis[u]=true;
        for(int i=0;i<g[u].size();i++)if(!vis[g[u][i]])dfs(g[u][i]);
        vs.pb(u); 
    }
    
    void rdfs(int u,int k)
    {
        vis[u]=true;
        cmp[u]=k;
        for(int i=0;i<rg[u].size();i++)if(!vis[rg[u][i]])rdfs(rg[u][i],k);
    } 
    
    int scc()
    {
        mem(vis,false);
        vs.clear();
        for(int i=0;i<n;i++)if(!vis[i])dfs(i);
        
        int k=0;
        mem(vis,false);
        for(int i=vs.size()-1;i>=0;i--)if(!vis[vs[i]])rdfs(vs[i],k++);
        return k; 
    }
    
    int DFS(int u)
    {
        if(!vis[u])
        {
            int t=0;
            vis[u]=true;
            for(int i=0;i<ng[u].size();i++)
            t=max(t,DFS(ng[u][i]));
            value[u]+=t; 
        } 
        return value[u];
    }
    
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
        while(cin>>n>>m)
        {
            init();
            for(int i=0;i<n;i++)cin>>a[i];
            for(int i=0;i<m;i++)cin>>u>>v,add_edge(u,v);
            int t=scc();
            for(int i=0;i<n;i++)
            {
                for(int j=0;j<g[i].size();j++)
                if(cmp[i]!=cmp[g[i][j]])ng[cmp[i]].pb(cmp[g[i][j]]),in[cmp[g[i][j]]]++;
                if(a[i]>0)value[cmp[i]]+=a[i];
            }
            mem(vis,false);
            int ans=0;
            for(int i=0;i<t;i++)if(in[i]==0)ans=max(ans,DFS(i)); 
            cout<<ans<<endl;
            //cout<<value[0]<<endl;
        }
        return 0;
    } 
    View Code
  • 相关阅读:
    SpringBoot-Maven打包压缩瘦身
    Docker安装Jenkins
    Spring Boot 微服务应用集成Prometheus + Grafana 实现监控告警
    Spring Boot构建 RESTful 风格应用
    SpringMVC 中 @ControllerAdvice 注解
    Spring Boot 整合 Freemarker
    Spring Boot中的静态资源文件
    SpringBoot配置文件 application.properties,yaml配置
    代码质量管理-安全问题
    8.Python基础 面向对象的基本概念
  • 原文地址:https://www.cnblogs.com/widsom/p/7637280.html
Copyright © 2011-2022 走看看