1. 程式人生 > >2017暑假訓練之並查集

2017暑假訓練之並查集

開學了,事情也越來越多了。我會努力補完的。
並查集基礎應用
HDUOJ 1232 暢通工程
int f[maxn], _rank[maxn];

void init(int n) {
	for (int i = 1; i <= n; ++i) f[i] = i, _rank[i] = 0;
}

//路徑壓縮的遞迴及非遞迴寫法
int find(int a) {
	//return f[a] == a ? a : f[a] = find(f[a]);
	int root = a;
	//尋找root
	while (f[root] != root) root = f[root];
	int cur = a;
	//改變f[cur]
	while (cur != root) {
		int temp = f[cur];
		f[cur] = root;
		cur = temp;
	}
	return f[a];
}

//按秩歸併
void _union(int a, int b) {
	int fa = find(a), fb = find(b);
	if (fa == fb) return;
	if(_rank[fa] < _rank[fb]) f[fa] = fb;
	else {
		f[fb] = fa;
		if (_rank[fa] == _rank[fb]) _rank[fa]++;
	}
}

int main() {
	int n, m;
	while (scanf("%d", &n) && n) {
		init(n);
		scanf("%d", &m);
		while (m--) {
			int a, b;
			scanf("%d%d", &a, &b);
			_union(a, b);
		}
		int ans = 0;
		for (int i = 1; i <= n; ++i) 
			if (f[i] == i) ans++;
		printf("%d\n", ans - 1);
	}
	return 0;
}
並查集的基礎應用,蠻細節的一道題
HDUOJ 1272 小希的迷宮
int f[maxn]; 
vector<int> vis;

void init() {
	vis.clear();
	for (int i = 1; i <= 100000; ++i) f[i] = i;
}

int find(int n) {
	//return f[n] == n ? n : f[n] = find(f[n]);
	int root = n;
	while (root != f[root]) root = f[root];
	int cur = n;
	while (cur != root) {
		int temp = f[cur];
		f[cur] = root;
		cur = temp;
	}
	return f[n];
}

bool _union(int a, int b) {
	int fa = find(a), fb = find(b);
	if (fa == fb) return false;
	f[fa] = fb;
	return true;
}

int main() {
	int a, b;
	while (scanf("%d%d", &a, &b)) {
		if (a == -1 && b == -1) break;
		if (a == 0 && b == 0) {
			printf("Yes\n");
			continue;
		}
		init();
		_union(a, b);
		vis.push_back(a);
		vis.push_back(b);
		bool ok = true;
		while (scanf("%d%d", &a, &b) && a) {
			//printf("fa = %d, fb = %d\n", f[a], f[b]);
			if (!_union(a, b)) ok = false;
			vis.push_back(a);
			vis.push_back(b);
		}
		//sort(vis.begin(), vis.end());
		vis.resize(unique(vis.begin(), vis.end()) - vis.begin());
		if (ok) {
			int cnt = 0;
			for (int i = 0; i < vis.size(); ++i)
				if (f[vis[i]] == vis[i]) cnt++;
			if (cnt > 1) ok = false;
		}
		if (ok) printf("Yes\n");
		else printf("No\n");
	}
	return 0;
}
經典帶權並查集
POJ 1182 食物鏈
向量偏移做法,並查集程式碼真的清晰美好哇!
int f[maxn], rela[maxn];

void init(int n) {
	for (int i = 1; i <= n; ++i) f[i] = i, rela[i] = 0;
}

int find(int a) {
	if (f[a] == a) return a;
	int t = f[a];
	f[a] = find(f[a]);
	rela[a] = (rela[a] + rela[t]) % 3;
	return f[a];
}

void _union(int a, int b, int re) {
	int fa = find(a), fb = find(b);
	f[fa] = fb;
	rela[fa] = ((re + rela[b] - rela[a]) % 3 + 3) % 3;
}

int main() {
	int n, k;
	scanf("%d%d", &n, &k);
	init(n);
	int op, a, b, cnt = 0;
	while (k--) {
		scanf("%d%d%d", &op, &a, &b);
		if (a > n || a < 1 || b > n || b < 1) ++cnt;
		else if (op == 1 && find(a) == find(b) && rela[a] != rela[b]) ++cnt;
		else if (op == 2 && find(a) == find(b) && ((rela[a] - rela[b])%3+3)%3 != 1) ++cnt;
		else _union(a, b, op - 1);
	}
	printf("%d\n", cnt);
	return 0;
}
種類並查集的做法,也十分清晰
int f[3 * maxn];

int find(int x) {
	return x == f[x] ? x : f[x] = find(f[x]);
}

void unite(int a, int b) {
	int fa = find(a), fb = find(b);
	if (fa == fb) return;
	f[fa] = fb;
}

void init(int n) {
	for (int i = 1; i <= 3 * n; ++i) f[i] = i;
}

int main() {
	int n, q;
	scanf("%d%d", &n, &q);
	init(n);
	int op, a, b, ans = 0;
	while (q--) {
		scanf("%d%d%d", &op, &a, &b);
		if (a < 1 || a > n || b < 1 || b > n) ans++;
		else if (op == 1) {
			if (find(a) == find(b + n) || find(a) == find(b + 2 * n)) ans++;
			else unite(a, b), unite(a + n, b + n), unite(a + 2 * n, b + 2 * n);
		}
		else if (op == 2) {
			if (find(a) == find(b) || find(a) == find(b + 2 * n)) ans++;
			else unite(a, b + n), unite(a + n, b + 2 * n), unite(a + 2 * n, b);
		}
	}
	printf("%d\n", ans);
	return 0;
}
種類並查集的變式
HDUOJ 3038 How Many Answers Are Wrong
int f[maxn], res[maxn];

void init(int n) {
	for (int i = 0; i <= n; ++i) f[i] = i, res[i] = 0;
}

int find(int x) {
	int t = f[x];
	if (t == x) return x;
	f[x] = find(t);
	res[x] = res[x] + res[t];
	return f[x];
}

void unite(int a, int b, int sum) {
	int la = find(a), lb = find(b);
	if (la < lb) f[lb] = la, res[lb] = res[a] + sum - res[b];
	else f[la] = lb, res[la] = res[b] - sum - res[a];
}

int main() {
	int n, q;
	while (~scanf("%d%d", &n, &q)) {
		init(n);
		int l, r, sum, ans = 0;
		while (q--) {
			scanf("%d%d%d", &l, &r, &sum); --l;
			if (find(l) == find(r) && res[r] - res[l] != sum) ans++;
			else unite(l, r, sum);
		}
		printf("%d\n", ans);
	}
	return 0;
}