NYOJ-120 校园网络 &&POJ 1236 (强连通缩点targan算法)

链接:click here

题意:

校园网络

时间限制:3000 ms  |  内存限制:65535 KB

难度:5

描述

南阳理工学院共有M个系,分别编号1~M,其中各个系之间达成有一定的协议,如果某系有新软件可用时,该系将允许一些其它的系复制并使用该软件。但该允许关系是单向的,即:A系允许B系使用A的软件时,B未必一定允许A使用B的软件。

现在,请你写一个程序,根据各个系之间达成的协议情况,计算出最少需要添加多少个两系之间的这种允许关系,才能使任何一个系有软件使用的时候,其它所有系也都有软件可用。

输入
第一行输入一个整数T,表示测试数据的组数(T<10)

每组测试数据的第一行是一个整数M,表示共有M个系(2<=M<=100)。

随后的M行,每行都有一些整数,其中的第i行表示系i允许这几个系复制并使用系i的软件。每行结尾都是一个0,表示本行输入结束。如果某个系不允许其它任何系使用该系软件,则本行只有一个0.

输出
对于每组测试数据,输出最少需要添加的这种允许关系的个数。
样例输入
1
5
2 4 3 0
4 5 0
0
0
1 0
样例输出
2

这道题第一感觉和图的入度出度有关,最后写了一下,数据比较水。

思路: 统计入度和出度就行了,一个顶点既有出度也有入度就表示此顶点不需要添加线路,最后取最大值比较即可。

代码:

#include <math.h>
#include <queue>
#include <deque>
#include <vector>
#include <stack>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
using namespace std;
#define Max(a,b) a>b?a:b
#define Min(a,b) a>b?b:a
#define mem(a,b) memset(a,b,sizeof(a))
int dir[4][2]= {{1,0},{-1,0},{0,1},{0,-1}};
const double eps = 1e-6;
const double Pi = acos(-1.0);
static const int INF = ~0U >> 2;
static const int MAXN = 102;
int mapp[MAXN][MAXN],comeder[MAXN],outder[MAXN];
int n,m,i,j,cas,cnt;
void input()
{
    for(i=1; i<=n; i++)
        while(~scanf("%d",&m),m)
        {
            mapp[i][m]=1;
        }
    for(i=1; i<=n; i++)
        for(j=1; j<=n; j++)
        {
            if(mapp[i][j])
            {
                comeder[j]=1;
                outder[i]=1;
            }
        }
}
void output()
{
    int sum1=0,sum2=0;
    for(i=1; i<=n; i++)
    {
        if(comeder[i]==0)
            sum1++;
        if(outder[i]==0)
            sum2++;
    }
    printf("%d\n",Max(sum1,sum2));
}
int main()
{
    scanf("%d",&cas);
    while(cas--)
    {
        int sum;
        scanf("%d",&n);
        mem(mapp,0),mem(comeder,0),mem(outder,0);
        input();
        output();
    }
    return 0;
}

后来看下题解,原来是强连通缩点targan算法,强连通缩点还没有学习,先贴上别人已过的代码,研究~~

解析(转):点击打开链接

Tarjan算法详解

【功能】

Tarjan算法的用途之一是,求一个有向图G=(V,E)里极大强连通分量。强连通分量是指有向图G里顶点间能互相到达的子图。而如果一个强连通分量已经没有被其它强通分量完全包含的话,那么这个强连通分量就是极大强连通分量。

【算法思想】

用dfs遍历G中的每个顶点,通dfn[i]表示dfs时达到顶点i的时间,low[i]表示i所能直接或间接达到时间最小的顶点。(实际操作中low[i]不一定最小,但不会影响程序的最终结果)

程序开始时,order初始化为0,在dfs遍历到v时,low[v]=dfn[v]=order++,v入栈(这里的栈不是dfs的递归时系统弄出来的栈)扫描一遍v所能直接达到的顶点k,如果 k没有被访问过那么先dfs遍历k,low[v]=min(low[v],low[k]);如果k在栈里,那么low[v]=min(low[v],dfn[k])(就是这里使得low[v]不一定最小,但不会影响到这里的low[v]会小于dfn[v])。扫描完所有的k以后,如果low[v]=dfn[v]时,栈里v以及v以上的顶点全部出栈,且刚刚出栈的就是一个极大强连通分量。

【大概的证明】

1.  在栈里,当dfs遍历到v,而且已经遍历完v所能直接到达的顶点时,low[v]=dfn[v]时,v一定能到达栈里v上面的顶点:

因为当dfs遍历到v,而且已经dfs递归调用完v所能直接到达的顶点时(假设上面没有low=dfn),这时如果发现low[v]=dfn[v],栈上面的顶点一定是刚才从顶点v递归调用时进栈的,所以v一定能够到达那些顶点。

2 .dfs遍历时,如果已经遍历完v所能直接到达的顶点而low[v]=dfn[v],我们知道v一定能到达栈里v上面的顶点,这些顶点的low一定小于 自己的dfn,不然就会出栈了,也不会小于dfn[v],不然low [v]一定小于dfn[v],所以栈里v以其v以上的顶点组成的子图是一个强连通分量,如果它不是极大强连通分量的话low[v]也一定小于dfn[v](这里不再详细说),所以栈里v以其v以上的顶点组成的子图是一个极大强连通分量。

【时间复杂度】

因为所有的点都刚好进过一次栈,所有的边都访问的过一次,所以时间复杂度为O(n+m)

代码如下:

// 强连通分量缩点
#include <iostream>
#include <cstring>
#include <cstdio>
#include <stack>

using namespace std;

const int MAX = 105;
int map[MAX][MAX];
int low[MAX], DFN[MAX], IN[MAX], OUT[MAX], instack[MAX], t[MAX];
int n, order, res, ans;
stack<int> S;

void init()
{
    memset(map, 0, sizeof(map));
    memset(low, 0, sizeof(low));
    memset(DFN, 0, sizeof(DFN));
    memset(IN, 0, sizeof(IN));
    memset(OUT, 0, sizeof(OUT));
    memset(instack, 0, sizeof(instack));
    memset(t, 0, sizeof(t));
    while(!S.empty())
       S.pop();
    res = 0;
    order = 0;
}

int min(int x, int y)
{
    return x < y ? x : y;
}

void tr(int u)
{
    int v;
    DFN[u] = low[u] = ++order;
    instack[u] = 1;
    S.push(u);
    for (int i = 1; i <= n; i++)
    {
        if(map[u][i])
        {
            if(!DFN[i])
            {
                tr(i);
                low[u] = min(low[u], low[i]);
            }
            else if(instack[i])
                low[u] = min(low[u], DFN[i]);
        }
    }
    if(DFN[u] == low[u])
    {
        ++res;    // res 代表强连通分量的个数
        do
        {
            v=S.top();
            S.pop();
            instack[v] = 0;
            t[v] = res;
        }while(v != u);
    }
}

void tarjan()
{
    for (int i = 1; i <= n; i++)
       if(!DFN[i])
           tr(i);
}

void solve()
{
    for (int i = 1;i <= n; i++)
    {
        for (int j = 1;j <= n; j++)
            if(map[i][j])  // 统计每个强连通分量缩点的入度和出度
            {
                ++IN[t[i]];
                ++OUT[t[j]];
            }
    }
    int xx, yy;
    xx = yy = 0;
    for(int i = 1; i <= res; i++)
    {
        if(IN[i]==0)
           xx++;
        else if(OUT[i]==0)
           yy++;
    }
    ans = xx > yy ? xx : yy; // 结果为缩点后的有向图中出度为0或者入度为0中的大者
}

int main()
{
    int T, x;
    scanf("%d", &T);
    while (T--)
    {
        init();
        scanf("%d",&n);
        for (int i = 1; i <= n; i++)
        {
            while (scanf("%d", &x), x)
                map[i][x] = 1;
        }
        tarjan();
        solve();
        if(res == 1)
           printf("0\n");
        else
           printf("%d\n", ans);
    }
    return 0;
}
时间: 2024-10-05 04:01:46

NYOJ-120 校园网络 &&POJ 1236 (强连通缩点targan算法)的相关文章

NYOJ 120 校园网络

校园网络 时间限制:3000 ms  |  内存限制:65535 KB 难度:5 描述 南阳理工学院共有M个系,分别编号1~M,其中各个系之间达成有一定的协议,如果某系有新软件可用时,该系将允许一些其它的系复制并使用该软件.但该允许关系是单向的,即:A系允许B系使用A的软件时,B未必一定允许A使用B的软件. 现在,请你写一个程序,根据各个系之间达成的协议情况,计算出最少需要添加多少个两系之间的这种允许关系,才能使任何一个系有软件使用的时候,其它所有系也都有软件可用. 输入 第一行输入一个整数T,

poj 2553强连通+缩点

/*先吐槽下,刚开始没看懂题,以为只能是一个连通图0T0 题意:给你一个有向图,求G图中从v可达的所有点w,也都可以达到v,这样的v称为sink.求这样的v. 解;求强连通+缩点.求所有出度为0的点即为要求的点. 注意:可能有多个联通分支. */ #include<stdio.h> #include<string.h> #include<stdlib.h> #define N 5100 struct node { int u,v,w,next; }bian[N*N*2]

POJ 1236 SCC+缩点

题意:一张有向图,一问至少给几个点发送软件,才能让所有点都能收到软件:二问是至少添加几条边才能让整个图是一个连通分量: 分析:一般求连通分量都会求缩点,在这里缩点之后,生成一张新的图,在新的图中求每一个点的出度,入度.答案就是sum(入度=0),max(sum(出度 == 0),sum(入度 == 0)); 注意:如果整张图本来就是一个强连通分量,需要特判.因为它出度,入度都等于0,即max(1,1) = 1,但是实际上不用再补充边了,应该是0,按照上面的分析答案就错了. 1 ///POJ123

POJ 1236 tarjan缩点+度数

Network of Schools Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 11441   Accepted: 4554 Description A number of schools are connected to a computer network. Agreements have been developed among those schools: each school maintains a li

poj2186 强连通缩点

Popular Cows Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 29141   Accepted: 11779 Description Every cow's dream is to become the most popular cow in the herd. In a herd of N (1 <= N <= 10,000) cows, you are given up to M (1 <= M &

POJ 1236 学校网络间的强连通

题目大意: N个学校之间有单向的网络,每个学校得到一套软件后,可以通过单向网络向周边的学校传输.问题1:初始至少需要向多少个学校发放软件,使得网络内所有的学校最终都能得到软件.问题2:至少需要添加几条传输线路(边),使任意向一个学校发放软件后,经过若干次传送,网络内所有的学校最终都能得到软件. 链接http://vjudge.net/problem/viewProblem.action?id=17001 每个强连通分量内只要有任意一个学校获得一份软件就可以了,因为强连通分量内的任意两点是相互可达

POJ 1236 Network of Schools(强连通 Tarjan+缩点)

POJ 1236 Network of Schools(强连通 Tarjan+缩点) ACM 题目地址:POJ 1236 题意: 给定一张有向图,问最少选择几个点能遍历全图,以及最少加入?几条边使得有向图成为一个强连通图. 分析: 跟HDU 2767 Proving Equivalences(题解)一样的题目,只是多了个问题,事实上转化成DAG后就不难考虑了,事实上仅仅要选择入度为0的点即可了. 代码: /* * Author: illuz <iilluzen[at]gmail.com> *

POJ 1236 Network of Schools(强连通分量)

POJ 1236 Network of Schools 链接:http://poj.org/problem?id=1236 题意:有一些学校连接到一个计算机网络.这些学校之间达成了一个协议:每个学校维护着一个学校列表,它向学校列表中的学校发布软件.注意,如果学校B 在学校A 的列表中,则A 不一定在B 的列表中. 任务A:计算为使得每个学校都能通过网络收到软件,你至少需要准备多少份软件拷贝. 任务B:考虑一个更长远的任务,想确保给任意一个学校发放一个新的软件拷贝,该软件拷贝能发布到网络中的每个学

POJ 1236 Network of Schools - 缩点

POJ 1236 :http://poj.org/problem?id=1236 参考:https://www.cnblogs.com/TnT2333333/p/6875680.html 题意: 有好多学校,每个学校可以给其他特定的学校发送文件.第一个问题是最少要给几个学校发文件,可以使得全部的学校收到文件.第二个问题是最少要加几条线路,使得随意挑一个学校发文件,也能使得全部的学校收到文件. 思路: 第一个问题,可以用tarjan给图中先缩点,因为强连通的环相互可达.所以只要数出缩完点后图中入度