Network of Schools

题意:摘自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)

5
2 4 3 0
4 5 0
0
0
1 0

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

1
2


解题思路:

  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.rene1
LANG: C
PROG: schlnet
 */

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define MAX_N 100

int 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;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值