ZOJ 3232 It's not Floyd Algorithm --强连通分量+Floyd

题意:给你一个传递闭包的矩阵,mp[u][v] = 1表示u可以到达v,为0代表不可到达,问你至少需要多少条边组成的传递闭包符合这个矩阵给出的关系

分析:考虑一个强连通分量,如果这个分量有n个节点,那么至少只需要n条边皆可以满足传递闭包(因为此时形成环就可),所以求出所有的强连通分量,将他们缩成一个个的点,并记录该强连通分量有多少个节点,然后建立新图,在运行一遍floyd算法,去除所有满足 tG[i][k]&&tG[k][j]&&tG[i][j]的边(i,j),然后统计还剩多少边,再加上每个强连通分量的节点数。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <stack>
using namespace std;
#define N 207

vector<int> G[N];
int mp[204][204],tG[204][204];
stack<int> stk;
int instk[N],cnt,Time,n;
int low[N],dfn[N],bel[N],num[N];

void tarjan(int u)
{
    low[u] = dfn[u] = ++Time;
    stk.push(u);
    instk[u] = 1;
    for(int i=0;i<G[u].size();i++)
    {
        int v = G[u][i];
        if(!dfn[v])
        {
            tarjan(v);
            low[u] = min(low[u],low[v]);
        }
        else if(instk[v])
            low[u] = min(low[u],dfn[v]);
    }
    if(low[u] == dfn[u])
    {
        cnt++;
        int v;
        do
        {
            v = stk.top();
            stk.pop();
            instk[v] = 0;
            bel[v] = cnt;
            num[cnt]++;
        }while(u != v);
    }
}

void Tarjan()
{
    memset(bel,0,sizeof(bel));
    memset(instk,0,sizeof(instk));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(num,0,sizeof(num));
    Time = cnt = 0;
    while(!stk.empty())
        stk.pop();
    for(int i=1;i<=n;i++)
        if(!dfn[i])
            tarjan(i);
}

void Build()
{
    int i,j;
    memset(tG,0,sizeof(tG));
    for(i=1;i<=n;i++)
    {
        for(j=0;j<G[i].size();j++)
        {
            int v = G[i][j];
            if(bel[i] != bel[v] && mp[i][v])
                tG[bel[i]][bel[v]] = 1;
        }
    }
}

int main()
{
    int i,j,k,u,v;
    while(scanf("%d",&n)!=EOF)
    {
        for(i=0;i<=n;i++)
            G[i].clear();
        for(i=1;i<=n;i++)
        {
            for(j=1;j<=n;j++)
            {
                scanf("%d",&mp[i][j]);
                if(i == j || !mp[i][j])
                    continue;
                G[i].push_back(j);
            }
        }
        Tarjan();
        Build();
        for(k=1;k<=cnt;k++)
        {
            for(i=1;i<=cnt;i++)
            {
                for(j=1;j<=cnt;j++)
                {
                    if(tG[i][j] && tG[i][k] && tG[k][j])
                        tG[i][j] = 0;
                }
            }
        }
        int res = 0;
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
                if(tG[i][j])
                    res++;
        for(i=1;i<=cnt;i++)
        {
            if(num[i] > 1)
                res += num[i];
        }
        printf("%d\n",res);
    }
    return 0;
}

ZOJ 3232 It's not Floyd Algorithm --强连通分量+Floyd

时间: 2024-10-05 05:41:40

ZOJ 3232 It's not Floyd Algorithm --强连通分量+Floyd的相关文章

(tarjan缩点+反floyd) zoj 3232

E - It's not Floyd Algorithm Time Limit:4000MS     Memory Limit:32768KB     64bit IO Format:%lld & %llu Submit Status Practice ZOJ 3232 Appoint description:  System Crawler  (2015-04-16) Description When a directed graph is given, we can solve its tr

(tajan+floyd) zoj 3232

It's not Floyd Algorithm Time Limit: 4 Seconds      Memory Limit: 32768 KB When a directed graph is given, we can solve its transitive closure easily using the well-known Floyd algorithm. But if you're given a transitive closure, can you give a corre

ZOJ 3795 Grouping 强连通分量-tarjan

一开始我还天真的一遍DFS求出最长链以为就可以了 不过发现存在有向环,即强连通分量SCC,有向环里的每个点都是可比的,都要分别给个集合才行,最后应该把这些强连通分量缩成一个点,最后保证图里是 有向无环图才行,这个时候再找最长链,当然缩点之后的scc是有权值的,不能只看成1,缩点完了之后,用记忆化搜索DP就可以再On的复杂度内求出结果 所以现学了一下SCC-Tarjan,所谓Scc-tarjan,就是找到强连通分量并且缩点,特别好用,其原理就是利用dfs时间戳,每个点有自己的时间戳,同时再开一个记

[POJ1236]Network of Schools(并查集+floyd,伪强连通分量)

题目链接:http://poj.org/problem?id=1236 这题本来是个强连通分量板子题的,然而弱很久不写tarjan所以生疏了一下,又看这数据范围觉得缩点这个事情可以用点到点之间的距离来判断不知道群巨兹磁不兹磁……下面弱就给大家搞一发如何用floyd和并查集来缩点……大致的思路就是先floyd跑出所有距离,然后O(n^2)找两两都可达的点,把它们的关系用并查集来维护.接下来O(n)找并查集里的代表元素.这个时候应当特判一下连通块为1的时候.再O(n^2)找出所有单向边,然后更新所有

floyd 闭包传递 判断两个点是否属于同一个 强连通分量

f[i][j]==true 代表 从i到j有 有向边相连 1 for(int k=0;k<n;k++) 2 for(int i=0;i<n;i++) 3 if(f[i][k]) 4 for(int j=0;j<n;j++) 5 if(f[k][j]) 6 f[i][j]=true; floyd 闭包传递 判断两个点是否属于同一个 强连通分量

[IOI1996] USACO Section 5.3 Network of Schools(强连通分量)

nocow上的题解很好. http://www.nocow.cn/index.php/USACO/schlnet 如何求强连通分量呢?对于此题,可以直接先用floyd,然后再判断. ---------------------------------------------------------------------------------- #include<cstdio> #include<iostream> #include<algorithm> #includ

POJ 2186 Popular Cows 强连通分量模板

题意 强连通分量,找独立的块 强连通分量裸题 #include <cstdio> #include <cstdlib> #include <cstring> #include <string> #include <algorithm> #include <iostream> using namespace std; const int maxn = 50005; int n, m; struct Edge { int v, next;

USACO network of school 强连通分量

这个题的意思是有一个有向图, 每个顶点可以发送软件到与其相连的顶点上, 现在问1,至少发送给几个顶点能满足所有顶点都收到软件, 2:如果想让这个图变成强连通图,至少添几条边.  特例是给定的图是一个强连通图的话答案是1, 0. 一般情况下我们先将这个图的强连通分量求出来缩成一个点然后统计入度为0的点和出度为0的点的个数, 答案一就是入度为0的点的个数, 答案就是他们两个之间的最大值.代码如下: /* ID: m1500293 LANG: C++ PROG: schlnet */ #include

POJ 2186:Popular Cows(强连通分量)

[题目链接] http://poj.org/problem?id=2186 [题目大意] 给出一张有向图,问能被所有点到达的点的数量 [题解] 我们发现能成为答案的,只有拓扑序最后的SCC中的所有点, 那么我们从其中一个点开始沿反图dfs,如果能访问到全图, 则答案为其所在SCC的大小,否则为0. [代码] #include <cstdio> #include <algorithm> #include <vector> #include <cstring>