题意:摘自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
解题思路:
- 参考了NOCOW上(http://www.nocow.cn/index.php/USACO/schlnet)“正确解法”的思想
- 首先求强连通分量,参考百度百科“强连通分量”条目
- 然后将强连通分量化为一个点,求各个强连通分量形成的新图
- 新图中入度为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;
}