“并查集”问题:抢银行问题


    有N个罪犯要抢银行,这些罪犯,有些单独作案,自己抢自己的,而有些合伙作案,抢到的钱合在一起。用 <i , j> (i != j)表示第i个罪犯和第j个罪犯为一组,例如<2 , 9> <5 , 9> 表示2、5、9三个罪犯为同一组现在有m个这样的 <i , j> 数据,表示N个罪犯的分组情况。现在抢银行已经结束,第 k 个罪犯抢到的钱为 money[ k ],求抢到最多的那一组的数。(单独作案的,自己一个人为一个组)

    这道题解决思路的关键是确定每个罪犯的分组。我们可以用一个集合表示一种分组,不断查找m个 <i , j> 数据,来确定所有分组的情况。显然这种“暴力”的方式不是我们想要的,其实这是一个经典的“并查集”问题。像抢银行这种寻找集合的问题,都是“并查集”问题,比如亲属关系问题、图的最小生成树问题。

    并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。并查集的核心代码只有3行,非常巧妙的且极大的降低了该类问题的时间复杂度。

    并查集解题思路是(以抢银行为例):

    1、用一个数组boss[]表示N个罪犯的各自的老大(即抢银行团伙组长),第i个罪犯的老大即boss[i];

    2、先将每个个体作为一个单独的集合,(自己是自己的老大)第i个罪犯的老大(boss[i] = i是自己

    3、利用一对数据 <i , j> (i != j),合并两个罪犯的老大(即第 i 个罪犯的老大   第j个罪犯的老大 的老大)boss[ boss[j] ] =boss[i] ,俗话“一山不容二虎”,顶级老大不能是两个,讲究“先来后道”,让先来i的老大成为当前分组的顶级老大

    4、更新自己的boss[j],让第j个罪犯的boss[j]更新为当前分组的顶级老大;

    5、如果还有数据 <i , j>,调转到3执行,否则,执行6;

    6、分组结束,处理其它情况,这里是统计最大总钱数的团伙的钱数。



代码如下:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 并查集,3行核心代码
int getboss(vector<int>& boss, int k)
{
	if (boss[k] == k) return k;// 如果第 k 个罪犯的老大是他自己,那么就返回他自己,即返回当前分组的顶级老大
	boss[k] = getboss(boss, boss[k]);// 如果第 k 个罪犯的老大不是他自己,那么就去获得老大的到大,即获得当前分组的顶级老大
	return boss[k];// 更新自己的顶级老大,返回当前分组的顶级老大
}

int calculateMoney(vector<vector<int> >& crimPair, vector<int>& money)
{
	vector<int> boss;// 记录老大的数组
	for (int i = 0; i <= money.size(); i++)
		boss.push_back(i);// 初始化老大数组,自己是自己的老大
	for (int i = 0, boss1 = 0, boss2 = 0; i < crimPair.size(); i++) {
		boss1 = getboss(boss, crimPair[i][0]);// 得到 0 罪犯所在分组的顶级老大
		boss2 = getboss(boss, crimPair[i][1]);// 得到 1 罪犯所在分组的顶级老大
		if (boss1 != boss2) boss[boss2] = boss1;// 如果两个老大不同,则合并老大,让先来的老大成为当前分组的顶级老大
	}
	for (int i = 1; i <= money.size(); i++) {
		boss[i] = getboss(boss, boss[i]);// 更新自己的顶级老大,然后进行其它处理
		if (boss[i] != i)
			money[boss[i] - 1] += money[i - 1];
	}

	return *max_element(money.begin(), money.end());
}

int main()
{
	vector<vector<int> > crimPair(6, vector<int>(2));
	crimPair[0][0] = 1; crimPair[0][1] = 2;
	crimPair[1][0] = 2; crimPair[1][1] = 3;
	crimPair[2][0] = 7; crimPair[2][1] = 8;
	crimPair[3][0] = 5; crimPair[3][1] = 6;
	crimPair[4][0] = 7; crimPair[4][1] = 9;
	crimPair[5][0] = 4; crimPair[5][1] = 5;
	vector<int> money;
	money.push_back(25);
	money.push_back(34);
	money.push_back(23);
	money.push_back(45);
	money.push_back(16);
	money.push_back(51);
	money.push_back(29);
	money.push_back(38);
	money.push_back(47);
	cout << calculateMoney(crimPair, money) << endl;
	return 0;
}

    原文作者:犯罪团伙问题
    原文地址: https://blog.csdn.net/u013683021/article/details/77601025
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞