ACM Learning Languages

Description

The “BerCorp” company has got n employees. These employees can use m approved official languages for the formal correspondence. The languages are numbered with integers from 1 to m. For each employee we have the list of languages, which he knows. This list could be empty, i. e. an employee may know no official languages. But the employees are willing to learn any number of official languages, as long as the company pays their lessons. A study course in one language for one employee costs 1 berdollar.
Find the minimum sum of money the company needs to spend so as any employee could correspond to any other one (their correspondence can be indirect, i. e. other employees can help out translating).

Input
The first line contains two integers n and m (2 ≤ n, m ≤ 100) — the number of employees and the number of languages.
Then n lines follow — each employee’s language list. At the beginning of the i-th line is integer ki (0 ≤ ki ≤ m) — the number of languages the i-th employee knows. Next, the i-th line contains ki integers — aij (1 ≤ aij ≤ m) — the identifiers of languages the i-th employee knows. It is guaranteed that all the identifiers in one list are distinct. Note that an employee may know zero languages.
The numbers in the lines are separated by single spaces.

Output
Print a single integer — the minimum amount of money to pay so that in the end every employee could write a letter to every other one (other employees can help out translating).

Sample Input
Input
5 5
1 2
2 2 3
2 3 4
2 4 5
1 5
Output
0

Input
8 7
0
3 1 2 3
1 1
2 5 4
2 6 7
1 3
2 7 4
1 1
Output
2

Input
2 2
1 2
0
Output
1

拿到这条题目,思路其实很简单,为了让员工可以相互沟通,即判断一个图有多少个连通子图。一般的,如果有n个连通子图,则需要学习的课程为n-1。
当然,这条题目有一种例外的情形,如果n个员工都不会说任意一种语言,则需要学习的课程就为n。
分析步骤如下:
1.如果n个员工均不会说任意一种语言,直接输出n;
2.否则,根据两两员工的状态,确定两点是否是连通的(有共同语言就连通,否则不连通)。
4.根据DFS,判断连通子图数目的大小,输出其减去1的值即可。

全局变量如下定义:

int graph[105][105];
int visited[105];
int language[105][105];
int n,m;
int piece;

分别是图、是否遍历的判断点、每个员工的语言学习情况表、图节点数目、语言数目、连通子图数目。
计算连通子图数目的DFS算法如下所示:

void traverse_r(int id)
{
    int i;
    visited[id]=1;
    for(i=0;i<n;i++)
    {
        if(graph[id][i] && !visited[i])
            traverse_r(i);
    }
}

void traverse()
{
    int i;
    for(i=0;i<n;i++)
    {
        if(!visited[i])
        {
            traverse_r(i);
            piece++;
        }
    }
}

图的构造算法如下所示:

for(i=0;i<n;i++)
        for(j=0;j<n;j++)
        {
            for(k=0;k<105;k++)
                if(language[i][k] && language[j][k])
                {
                    graph[i][j]=graph[j][i]=1;
                    break;
                }
        }

最后统一一下所有代码:

#include<stdio.h>
#include<math.h>
#include<string.h>
#define MIN(x,y) ((x)<(y)?(x):(y))
#define MAX(x,y) ((x)>(y)?(x):(y))
#define ABS(x) (((x)>0)?(x):(-(x)))
#define bool int
#define true 1
#define false 0
#define TRUE 1
#define FALSE 0
#define int64 long long

#define MAXLEN 1000005

int graph[105][105];
int visited[105];
int language[105][105];
int n,m;
int piece;


void traverse_r(int id)
{
    int i;
    visited[id]=1;
    for(i=0;i<n;i++)
    {
        if(graph[id][i] && !visited[i])
            traverse_r(i);
    }
}

void traverse()
{
    int i;
    for(i=0;i<n;i++)
    {
        if(!visited[i])
        {
            traverse_r(i);
            piece++;
        }
    }
}

int main()
{
    int i,j,k,x;
    int allzero=1;
    scanf("%d%d",&n,&m);
    memset(graph,0,sizeof(graph));
    memset(language,0,sizeof(language));
    for(i=0;i<n;i++)
    {
        scanf("%d",&k);
        if(k>0) allzero=0;
        for(j=0;j<k;j++)
        {
            scanf("%d",&x);
            language[i][x]=1;
        }
    }
    if(allzero)
    {
        printf("%d\n",n);
        return 0;
    }
    for(i=0;i<n;i++)
        for(j=0;j<n;j++)
        {
            for(k=0;k<105;k++)
                if(language[i][k] && language[j][k])
                {
                    graph[i][j]=graph[j][i]=1;
                    break;
                }
        }
    traverse();
    printf("%d\n",piece-1);
    return 0;
}

运行结果:Accept

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值