Network of Schools

来源:互联网 发布:linux安装mrtg详解 编辑:程序博客网 时间:2024/06/05 15:55

题意:摘自NOCOW翻译(http://www.nocow.cn/index.php/Translate:USACO/schlnet)

描述

一些学校连入一个电脑网络。那些学校已订立了协议:每个学校都会给其它的一些学校分发软件(称作“接受学校”)。注意即使 B 在 A 学校的分发列表中, A 也不一定在 B 学校的列表中。

你要写一个程序计算,根据协议,为了让网络中所有的学校都用上新软件,必须接受新软件副本的最少学校数目(子任务 A)。更进一步,我们想要确定通过给任意一个学校发送新软件,这个软件就会分发到网络中的所有学校。为了完成这个任务,我们可能必须扩展接收学校列表,使其加入新成员。计算最少需要增加几个扩展,使得不论我们给哪个学校发送新软件,它都会到达其余所有的学校(子任务 B)。一个扩展就是在一个学校的接收学校列表中引入一个新成员。

[编辑]格式

PROGRAM NAME: schlnet

INPUT FORMAT 输入文件的第一行包括一个整数 N:网络中的学校数目(2 <= N <= 100)。学校用前 N 个正整数标识。接下来 N 行中每行都表示一个接收学校列表(分发列表)。第 i+1 行包括学校 i 的接收学校的标识符。每个列表用 0 结束。空列表只用一个 0 表示。

OUTPUT FORMAT

你的程序应该在输出文件中输出两行。第一行应该包括一个正整数:子任务 A 的解。第二行应该包括子任务 B 的解。

[编辑]SAMPLE INPUT (file schlnet.in)

52 4 3 04 5 0001 0

[编辑]SAMPLE OUTPUT (file schlnet.out)

12


解题思路:

  1. 参考了NOCOW上(http://www.nocow.cn/index.php/USACO/schlnet)“正确解法”的思想
  2. 首先求强连通分量,参考百度百科“强连通分量”条目
  3. 然后将强连通分量化为一个点,求各个强连通分量形成的新图
  4. 新图中入度为0的点个数为a,出度为0的点个数为b。那么第一问的答案就是a,第二问的答案就是max(a, b)。特殊情况就是新图仅有一个点,那么a = 1, b = 1,但是答案分别为1, 0

代码

/*ID: zc.rene1LANG: CPROG: schlnet */#include<stdio.h>#include<stdlib.h>#include<string.h>#define MAX_N 100int N;int adj_table[MAX_N + 1][MAX_N + 1];int radj_table[MAX_N + 1][MAX_N + 1];int visited[MAX_N + 1];int leave_time[MAX_N + 1];int scc[MAX_N + 1];int num_scc;void print_table(int table[MAX_N + 1][MAX_N + 1]){    int i, j;    for (i=1; i<=N; i++)    {printf("%d==>", i);for (j=1; j<=table[i][0]; j++){    printf("%d ", table[i][j]);}printf("\n");    }}void print_seq(int *a, char *str){    int i;    printf("%s: ", str);    for (i=1; i<=N; i++)    {printf("%d ", a[i]);    }    printf("\n");}void GetInput(FILE *fin){    int i, j;    fscanf(fin, "%d", &N);    memset(adj_table, 0, (MAX_N + 1) * (MAX_N + 1) * sizeof(int));    memset(radj_table, 0, (MAX_N + 1) * (MAX_N + 1) * sizeof(int));    for (i=1; i<=N; i++)    {while (fscanf(fin, "%d", &j) == 1){    if (j == 0)    {break;    }    adj_table[i][++adj_table[i][0]] = j;    radj_table[j][++radj_table[j][0]] = i;}    }}void VisitOne(int cur, int *sig){    int i;    visited[cur] = 1;    for (i=1; i<=adj_table[cur][0]; i++)    {if (visited[adj_table[cur][i]] == 0){    VisitOne(adj_table[cur][i], sig);}    }    (*sig)++;    leave_time[*sig] = cur;}void VisitTwo(int cur, int sig){    int i;    visited[cur] = 1;    scc[cur] = sig;    for (i=1; i<=radj_table[cur][0]; i++)    {if (visited[radj_table[cur][i]] == 0){    VisitTwo(radj_table[cur][i], sig);}    }}void KS(void){    int i, sig;    memset(visited, 0, (MAX_N + 1) * sizeof(int));    for (sig=0, i=1; i<=N; i++)    {if (visited[i] == 0){    VisitOne(i, &sig);}    }    memset(visited, 0, (MAX_N + 1) * sizeof(int));    for (sig=0, i=N; i>=1; i--)    {if (visited[leave_time[i]] == 0){    VisitTwo(leave_time[i], ++sig);}    }    num_scc = sig;}void CountDegree(FILE *fout){    int i, j, k;    int in_0, out_0;    int in[MAX_N + 1];    int out[MAX_N + 1];    memset(in, 0, (num_scc + 1) * sizeof(int));    memset(out, 0, (num_scc + 1) * sizeof(int));    for (i=1; i<=N; i++)    {for (j=1; j<=adj_table[i][0]; j++){    k = adj_table[i][j];    if (scc[i] != scc[k])    {out[scc[i]]++;in[scc[k]]++;    }}    }    in_0 = 0;    out_0 = 0;    for (i=1; i<=num_scc; i++)    {if (in[i] == 0){    in_0++;}if (out[i] == 0){    out_0++;}    }    if (num_scc == 1)    {fprintf(fout, "1\n0\n");    }    else    {fprintf(fout, "%d\n%d\n", in_0, in_0 > out_0 ? in_0 : out_0);    }}int main(void){    FILE *fin, *fout;    fin = fopen("schlnet.in", "r");    fout = fopen("schlnet.out", "w");        GetInput(fin);    KS();    CountDegree(fout);    return 0;}





原创粉丝点击