食物链 并查集

食物链 并查集

食物链题目链接
刚开始看题目的时候,不知道如何维护谁吃谁的关系,只知道维护谁是谁的同类。
还是把知识点学得太死了,不知道变通。
下面还有大佬的题解方便复习不懂的知识点。
有两种做法:

  1. 并查集数组开三倍的空间

食物链_牛客博客

  1. 带权并查集

AcWing 240. 食物链 - AcWing
AcWing 240. 食物链(带权并查集)

在这里安利一下ACwing y总的算法基础课和提高课讲的真得好

方法一

思路:将数组开3倍大,a表示自身的一层(即a类),a+n表示被a吃掉的一类,a+2n表示能吃a的一类。

#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define INF 0x3f3f3f3f
#define PII pair<int, int>
#define rep(i, l, r) for (int i = l; i < r; i++)
#define per(i, l, r) for (int i = l; i >= r; i--)
#define rep2(i, l, r) for (int i = l; i * i <= r; i++)
#define rep3(i, l, r) for (LL i = l; i * i * i <= r; i++)
#define Min(a, b) a > b ? b : a
#define Max(a, b) a > b ? a : b
#define endl '\n'
#define debug "-----"
using namespace std;
typedef long long LL;
const LL mod = 1e9;
const int N = 1e6 + 10, M = 10100;
LL gcd(LL a, LL b) { return b ? gcd(b, a % b) : a; }

int p[N*2],sum;
int n,k;
int d,x,y;
int ans;
map<int,int>mp;

int find( int x ){
	if( p[x] != x ) p[x] = find( p[x] );
	return p[x];
}

void merge_( int x , int y ){
	p[ find( x ) ] = find( y );
}

int main()
{
	IOS;
	cin >> n >> k;
	rep( i , 1 , 3*(n+1) ) p[i] = i;
	while( k-- ){
		cin >> d >> x >> y;
		if( x > n || y > n ){ ans++; continue; }
		if( d == 1 ){
			if( find(x) == find(y+n) || find(x) == find(y+2*n) ) { ans++; continue; }
			merge_( x , y );
			merge_( x+n , y+n );
			merge_( x+2*n,y+2*n );
		}
		else{			
			if( find(x) == find(y) || find(x) == find(y+2*n) ) { ans++;continue; }
			merge_( x , y+n );
			merge_( x+n , y+2*n );
			merge_( x+2*n , y );
		}
	}
	cout << ans;
	return 0;
}

方法二

思路:不断得维护昕的结点到父亲结点的距离,并根据距离%3后的大小来判断与两个点是什么关系。

1. 距离之差为0时表明是同类。
2. 距离之差为1时表明是不是关系。

d[x]数组维护的是该点X到他的父亲结点p[X]的距离

#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define INF 0x3f3f3f3f
#define PII pair<int, int>
#define rep(i, l, r) for (int i = l; i < r; i++)
#define per(i, l, r) for (int i = l; i >= r; i--)
#define rep2(i, l, r) for (int i = l; i * i <= r; i++)
#define rep3(i, l, r) for (LL i = l; i * i * i <= r; i++)
#define Min(a, b) a > b ? b : a
#define Max(a, b) a > b ? a : b
#define endl '\n'
#define debug "-----"
using namespace std;
typedef long long LL;
const LL mod = 1e9;
const int N = 1e6 + 10, M = 10100;
LL gcd(LL a, LL b) { return b ? gcd(b, a % b) : a; }

int p[N],d[N];
int n,k;
int x,y,d1;
int ans;

int find( int x ){
	if( p[x] != x ){
		int t = find(p[x]);
		d[x] += d[p[x]];
		p[x] = t;
	}
	return p[x];
}

int main()
{
	IOS;
	cin >> n >> k;
	rep( i , 1 , n+1 ) p[i] = i;
	while( k-- ){
		cin >> d1 >> x >> y;
		if( x > n || y > n ){ ans++; continue; }
		int px = find(x) , py = find(y);
		if( d1 == 1 ){
			if( px == py && (d[x]-d[y])%3 != 0 ) ans++;
			else if( px != py ) {
				p[px] = py;
				d[px] = d[y]-d[x];
			}
		}
		else{			
			if( px == py && (d[x]-d[y]-1)%3 != 0 ) ans++;
			else if( px != py ) {
				p[px] = py;
				d[px] = d[y]+1-d[x];
			}
		}
	}
	cout << ans;
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
并查集是一种用于解决集合合并与查询问题的数据结构。在食物链问题中,我们可以使用并查集来判断给定的K句话中有多少句是假话。 首先,我们需要创建一个并查集,其中每个动物都是一个节点。初始时,每个节点都是独立的集合。 然后,我们按照给定的K句话进行处理。对于每一句话,我们需要判断它是真话还是假话。 如果是第一种说法"1 X Y",表示X和Y是同类。我们可以通过将X和Y所在的集合合并来实现。即将X所在的集合的根节点指向Y所在的集合的根节点,或者将Y所在的集合的根节点指向X所在的集合的根节点。 如果是第二种说法"2 X Y",表示X吃Y。我们需要判断X和Y是否属于同一类。如果它们属于同一类,那么这句话就是假话。否则,这句话是真话。 最后,我们统计假话的总数即可。 以下是一个示例代码,演示了如何使用并查集解决食物链问题: ```python class UnionFind: def __init__(self, n): self.parent = list(range(n)) self.rank = [0] * n def find(self, x): if self.parent[x] != x: self.parent[x] = self.find(self.parent[x]) return self.parent[x] def union(self, x, y): root_x = self.find(x) root_y = self.find(y) if root_x == root_y: return if self.rank[root_x] < self.rank[root_y]: self.parent[root_x] = root_y elif self.rank[root_x] > self.rank[root_y]: self.parent[root_y] = root_x else: self.parent[root_y] = root_x self.rank[root_x] += 1 def count_false_statements(N, K, statements): uf = UnionFind(N+1) false_count = 0 for statement in statements: type, X, Y = statement if X > N or Y > N: false_count += 1 continue if type == 1: if uf.find(X) == uf.find(Y+N) or uf.find(X+N) == uf.find(Y): false_count += 1 else: uf.union(X, Y) uf.union(X+N, Y+N) uf.union(X+2*N, Y+2*N) elif type == 2: if uf.find(X) == uf.find(Y) or uf.find(X+N) == uf.find(Y): false_count += 1 else: uf.union(X, Y+N) uf.union(X+N, Y+2*N) uf.union(X+2*N, Y) return false_count N = 5 K = 7 statements = [(1, 1, 2), (2, 1, 3), (1, 2, 3), (2, 2, 4), (1, 4, 1), (1, 4, 3), (2, 4, 3)] false_count = count_false_statements(N, K, statements) print(false_count) # 输出:3 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值