并查集的简单例题

codeforce 731C:Socks http://codeforces.com/problemset/problem/731/C

题意:

        给n(袜子数),m(天数),k(颜色个数) 接下来的一行是n个袜子的颜色  接下来m行是每天需要穿的袜子. 你每天必须穿同种颜色的袜子,所以你必须给不同颜色的袜子进行染色. 求需要染色的最少次数

题解:

        将所有的需要穿的袜子link, 并且在所有袜子所在的联通分量中统计出现袜子颜色最多的个数, 用总的个数减去袜子颜色最多的就是每个联通分量所需要染色的最少个数, 将所有的联通分量的个数相加就可以得到最后的结果

代码:

#include<iostream>
#include<cstdio>
#include<string>
#include<string.h>
#include<algorithm>
#include<vector>
#include<map>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 200000 + 1000;
int n, m;
int par[MAXN];
int height[MAXN];
void init(){
	for (int i = 1; i <= n; i++){
		par[i] = i;
		height[i] = 0;
	}
}
int find(int x){
	if (x != par[x]) par[x] = find(par[x]);
	return par[x];
}
vector<int> vec[MAXN];

void link(int x, int y){
	x = find(x); y = find(y);
	if (x == y) return;
	par[y] = x;
}

int a[MAXN];
int main(){
	int k;
	while (scanf("%d%d%d", &n, &m, &k) != EOF){
		init();
		for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
		int L, R;
		for (int i = 0; i < m; i++){
			scanf("%d%d", &L, &R);
			link(L, R);
		}
		for (int i = 1; i <= n; i++){ //找到每个袜子所属于的集合,把他们对应的颜色存在vec中
			vec[find(i)].push_back(a[i]);
		}
		int sum = 0;
		map<int, int>::iterator it;
		for (int i = 1; i <= n; i++){
			if (!vec[i].size()) continue;
			map<int, int> mapp;
			int maxx = 0;
			for (unsigned j = 0; j < vec[i].size(); j++){
				mapp[vec[i][j]]++;
				maxx = max(maxx, mapp[vec[i][j]]);
			}
			sum += (vec[i].size() - maxx);
		}
		printf("%d\n", sum);
		for (int i = 1; i <= n; i++) vec[i].clear();
	}
	return 0;
}

codeforce 722C:Destroying Array http://codeforces.com/problemset/problem/722/C

题意:

     给一数字n,然后给两个序列.  第二个序列是将下标为bi的第一个序列对应位置的数去掉. 然后求去掉后的最大连续段和.

题解:

     从前往后是依次去掉.而从后往前是依次增加, 这就减低了难度.

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>
using namespace std;
typedef long long ll;
const int maxn = 100000 + 1000;
bool is[maxn];
ll maxx[maxn];
int p[maxn];
ll a[maxn];
ll ans[maxn];
int par[maxn];
int n;
void init(){
	for (int i = 1; i <= n; i++) par[i] = i;
	memset(is, false, sizeof(is));
	memset(maxx, 0, sizeof(maxx));
}
int find(int x){
	if (x == par[x]) return x;
	return par[x] = find(par[x]);
}
int main(){
	while (scanf("%d", &n) != EOF){
		init();
		for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
		for (int i = 1; i <= n; i++) scanf("%d", &p[i]);
		ans[n] = 0;
		for (int i = n; i > 1; i--){
			int p1 = p[i];
			maxx[p1] = a[p1];
			if (is[p1 - 1]){//看当前位置的左边是否已经增加
				int x1 = find(p1);//如果增加了那么就找到两者的父亲,然后将当前的父亲设为左边的父亲
				int x2 = find(p1 - 1);
				par[x1] = x2;
				maxx[x2] += maxx[x1];//将当前左边连续段的和与该值加起来
			}
			if (is[p1 + 1]){//同上
				int x1 = find(p1);
				int x2 = find(p1 + 1);
				par[x1] = x2;
				maxx[x2] += maxx[x1];
			}
			ans[i - 1] = max(maxx[find(p1)], ans[i]);//比较上次的答案和当前找到的谁大
			is[p1] = true;
		}
		for (int i = 1; i <= n; i++){
			printf("%lld\n", ans[i]);
		}
	}

	return 0;
}

codeforce 292D:Connected Components http://codeforces.com/problemset/problem/292/D

题意:

        给出n(点的个数 1...n)  m(边的个数) 给出m条边的两个端点  然后对其进行操作,  给出范围l,r  删除下标在l到r之间的边, 求删除以后联通分量的个数

题解:

       我在做题时的想法是 对于每次删除之后 对剩下的边进行并查集操作. 但是时间复杂度是O(m*n) 超时了

       给出范围l,r  那么就是对1...l-1 和 r+1...m 进行并查集操作, 我们可以通过预处理 将前i条边进行并查集并储存, 将后i条边进行并查集并储存 那么每次进行询问就可以将 dsuL[l-1]和dsuR[r+1] 这两个并查集进行link操作.

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>
using namespace std;
typedef long long ll;
const int MAXN = 1000;
const int MAXM = 10000 + 100;
int x[MAXM], y[MAXM];
int n, m;
struct DSU{
	int par[MAXN];
	int height[MAXN];
	void init(){
		for (int i = 1; i <= n; i++) par[i] = i;
		memset(height, 0, sizeof(height));
	}
	int find(int x){
		if (x == par[x]) return x;
		return par[x] = find(par[x]);
	}
	void link(int x, int y){
		x = find(x); y = find(y);
		if (x == y) return;
		if (height[x] > height[y]){
			par[y] = x;
		} else{
			par[x] = y;
			if (height[x] == height[y]) height[x]++;
		}
	}
}dsuL[MAXM], dsuR[MAXM], dsu;
int main(){
	while (scanf("%d%d", &n, &m) != EOF){
		for (int i = 1; i <= m; i++){
			scanf("%d%d", &x[i], &y[i]);
		}
		dsuL[0].init();
		for (int i = 1; i <= m; i++){
			dsuL[i] = dsuL[i - 1];
			dsuL[i].link(x[i], y[i]);
		}
		dsuR[m + 1].init();
		for (int i = m; i >= 0; i--){
			dsuR[i] = dsuR[i + 1];
			dsuR[i].link(x[i], y[i]);
		}
		int q;
		int a, b;
		scanf("%d", &q);
		while (q--){
			scanf("%d%d", &a, &b);
			dsu = dsuR[b + 1];
			for (int i = 1; i <= n; i++){
            //将dsuR[b+1]中每一个点和dsuR[a-1]中该点的父亲相连,那么如果本来par[i]=i 那么不变, 如果不是 那么连接
				dsu.link(i, dsuL[a - 1].find(i));
			}
			int sum = 0;
			for (int i = 1; i <= n; i++){
				if (dsu.par[i] == i) sum++;
			}
			printf("%d\n", sum);
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39513105/article/details/81805335
今日推荐