CF277A Learning Languages 解题报告

CF277A Learning Languages 解题报告

1 题目链接

https://codeforces.com/problemset/problem/277/A

2 题目整理

题目 : 学习语言

题目描述

BerCorp公司有 n n n名员工。这些员工可以使用经批准的官方语言进行正式通信。语言用从 1 1 1 m m m的整数进行编号。对于每个员工,我们都有他所知道的语言列表。这个列表可以是空的,即员工可能不懂官方语言。但员工愿意学习任何官方语言,只要公司支付他们的课程。一名员工参加一种语言的学习课程的费用是 1 1 1美元。

找出公司需要花费的最低金额,这样任何员工都可以与其他员工通信(他们的通信可以是间接的,也就是说,其他员工可以帮助翻译)。

输入格式

第一行两个整数 n , m n, m n,m,分别表示员工的数量与官方语言的种类数。

接下来 n n n行每行第一个整数是 k i k_i ki,表示第 i i i位员工的已学语言的种类数。接下来有 k i k_i ki个整数,第 j j j个整数 a i , j a_{i,j} ai,j表示第 i i i位员工第 j j j个已会语言的编号。

输出格式

一行一个整数,表示公司想让任意两个员工可以交流所需要的最小花费。

样例输入1
5 5
1 2
2 2 3
2 3 4
2 4 5
1 5
样例输出1
0
样例输入2
8 7
0
3 1 2 3
1 1
2 5 4
2 6 7
1 3
2 7 4
1 1
样例输出2
2
样例输入3
2 2
1 2
0
样例输出3
1
数据范围

对于 100 % 100\% 100%的数据:

  • 2 ≤ n , m ≤ 100 2 \leq n, m \leq 100 2n,m100
  • 0 ≤ k i ≤ m 0 \leq k_i \leq m 0kim
  • 1 ≤ a i , j ≤ m 1 \leq a_{i,j} \leq m 1ai,jm

3 题意分析

3.1 题目大意

n n n个人, m m m种语言,每个人会 k i k_i ki种语言,有相同语言的可以直接沟通。现在问至少要让一些人多学几种语言,才能使所有人能自由交谈。

3.2 样例分析

如上所述。

4 解法分析

一道普通的建图+并查集简单题目。

首先,因为会相同语言的两个人可以直接沟通,所以把这两个人连起来。按照这样的规则用并查集建图,最后会有 p p p个连通块。如果其中有一个人会一种语言以上,那么答案为 p − 1 p-1 p1,就是让其他人都学这种语言;否则答案为 p p p,就是所有人都学一种语言。

AC代码

ACCode #001
// From Heart_Blue
// Rating 2425
// reason : 思路清晰代码简洁明了,运用了Class + 并查集
#include <cstdlib>
#include <cctype>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <string>
#include <iostream>
#include <sstream>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <fstream>
#include <numeric>
#include <iomanip>
#include <bitset>
#include <list>
#include <stdexcept>
#include <functional>
#include <utility>
#include <ctime>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#define MEM(a,b) memset((a),(b),sizeof(a))
const LL INF = 1e9 + 7;
const int N = 2e2 + 10;
int flag[N];
class UnionFind
{
private:
	int p[N];
public:
	int size(int x)
	{
		int px = Find(x);
		return -p[px];
	}
	void init()
	{
		MEM(p, -1);
	}
	int Find(int x)
	{
		int root = x;
		while (p[root] >= 0) root = p[root];
		while (x != root)
		{
			int tmp = p[x];
			p[x] = root;
			x = tmp;
		}
		return root;
	}
	void Union(int x, int y)
	{
		int px = Find(x);
		int py = Find(y);
		if (size(px) > size(py)) swap(px, py);
		int total = size(px) + size(py);
		p[px] = py;
		p[py] = -total;
	}
} uf;
int main()
{
	//freopen("input.txt", "r", stdin);
	//freopen("output.txt", "w", stdout);
	uf.init();
	MEM(flag, 0);
	int n, m;
	cin >> n >> m;
	int f = 0;
	for (int i = 1; i <= n; i++)
	{
		int tot;
		cin >> tot;
		if (tot > 0) f = 1;
		while (tot--)
		{
			int x;
			cin >> x;
			if (uf.Find(x + n) != uf.Find(i))
			{
				uf.Union(x + n, i);
			}
		}
	}
	int ans = 0;
	for (int i = 1; i <= n; i++)
	{
		int fa = uf.Find(i);
		if (!flag[fa]) ans++;
		flag[fa] = 1;
	}
	cout << ans - f << endl;
	return 0;
}
ACCode #002
// From snuke
// Rating 3099
// reason : 思路清晰代码简洁明了,运用了并查集
#include<cstdio>
#include<algorithm>
#include<stack>
#include<queue>
#include<vector>
#include<string>
#include<string.h>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<map>
#include<set>
#include<bitset>
#include<iostream>
#include<sstream>
#define fi first
#define se second
#define rep(i,n) for(int i = 0; i < n; i++)
#define rrep(i,n) for(int i = 1; i <= n; i++)
#define drep(i,n) for(int i = n-1; i >= 0; i--)
#define each(c,it) for(__typeof((c).begin()) it=(c).begin();it!=(c).end();it++)
#define rng(a) a.begin(),a.end()
#define maxs(x,y) x = max(x,y);
#define mins(x,y) x = min(x,y);
#define pb push_back
#define snuke srand((unsigned)time(NULL))
using namespace std;
typedef long long int ll;
typedef pair<int,int> P;

const int MX = 105, INF = 1000000000, mod = 1000000009;
const ll LINF = 1000000000000000000ll;
const int dx[4] = {0,-1,1,0}, dy[4] = {-1,0,0,1};

// Union find
struct uf{
	vector<int> par, rank;
	uf(){}
	uf(int sz){ par.resize(sz); rank.resize(sz); init(sz);}
	void init(int sz){ rep(i,sz) par[i] = i, rank[i] = 1;}
	int pa(int x){
		if(par[x] == x) return x;
		return par[x] = pa(par[x]);
	}
	void uni(int x, int y){
		x = pa(x); y = pa(y);
		if(rank[x] < rank[y]) swap(x,y);
		par[y] = x;
		if(rank[x] == rank[y]) rank[x]++;
	}
};
//

int l[MX]; bool b[MX];

int main(){
	int n, m, k, ans = 0;
	scanf("%d%d",&n,&m);
	
	uf t(m+1);
	rrep(i,m) b[i] = false;
	rep(i,n){
		scanf("%d",&k);
		rep(j,k){
			scanf("%d",&l[j]);
			rep(h,j) t.uni(l[j],l[h]);
			b[l[j]] = true;
		}
		if(k == 0) ans++;
	}
	
	bool bl = false;
	rrep(i,m){
		if(b[i] && t.pa(i) == i){
			if(bl) ans++;
			bl = true;
		}
	}
	
	printf("%d\n",ans);
	
	return 0;
}
ACCode #003
// From rng_58
// Rating 3074
// reason : 思路清晰代码简洁明了,运用了并查集
#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <vector>
#include <deque>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <algorithm>
#include <functional>
#include <utility>
#include <bitset>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <cstdio>

using namespace std;

#define REP(i,n) for((i)=0;(i)<(int)(n);(i)++)
#define snuke(c,itr) for(__typeof((c).begin()) itr=(c).begin();itr!=(c).end();itr++)

#define MAXV 110

int parent[MAXV],rank[MAXV];

void init(int n){
    int i;
    REP(i,n) {parent[i] = i; rank[i] = 1;}
}

int root(int x){
    if(parent[x] != x) parent[x] = root(parent[x]);
    return parent[x];
}

void connect(int x, int y){
    int rx=root(x),ry=root(y);
    if(rx == ry) return;
    if(rank[rx] > rank[ry]) {parent[ry] = rx; rank[rx] += rank[ry];}
    if(rank[rx] <= rank[ry]) {parent[rx] = ry; rank[ry] += rank[rx];}
}

bool a[110][110];

int main(void){
    int N,M,K,i,j,k,x;
    
    cin >> N >> M;
    REP(i,N){
        cin >> K;
        REP(j,K){
            cin >> x;
            a[i][x-1] = true;
        }
    }
    
    bool nonzero = false;
    REP(i,N) REP(j,M) if(a[i][j]) nonzero = true;
    if(!nonzero){
        cout << N << endl;
        return 0;
    }
    
    init(N);
    
    REP(i,N) REP(j,N) if(i < j){
        bool found = false;
        REP(k,M) if(a[i][k] && a[j][k]) found = true;
        if(found){
            connect(i, j);
        //  cout << i << ' ' << j << endl;
        }
    }
    
    int comp = 0;
    REP(i,N) if(root(i) == i) comp++;
    cout << comp - 1 << endl;
    
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值