http://acm.hdu.edu.cn/showproblem.php?pid=3047
#Description
有一个体育馆,每行300个坐位
来了N个人,提出M组要求
要求格式为 A B X
假设A的坐位是i, 则B的坐位是 i + X
如果来了一组要求和之前矛盾,忽略这组要求
问最终由多少组矛盾的要求
#Algorithm
带权并查集
用权V[i]表示X[id[i]] - X[i]
X[i] 为 第i个人做的位置
查找(find)时,启用路径压缩,则
v[p] = v[p] + v[t] t为之前的id[p]
因为路径压缩之后,id[p] == id[t] 假设 为 root
v[p] = x[root] - x[p]
v[t] = x[root] - x[t]
一开始
v[p] = x[t] - x[p]
所以 v[p] + v[t] = 新的v[p]
合并(unite)时,找的p, q的根i, j
如果 i == j
由于启用了路径压缩
v[p] = x[root] - x[p]
v[q] = x[root] - x[q]
定义 unite传入的参数x为 x[q] - x[p],
v[p] - v[q] = x[q] - x[p] 与 x 比较 不等则表示 这条信息矛盾
如果 i != j
那么就要根进行变化
使用加权并查集树的权值sz
假定sz[i] > sz[j] 即 root = i 的是大树,要将j 并到 i上去
id[j] = i;
sz[i] += sz[j];
关键是v[j]的变化
变化的v[j] = x[i] - x[j]
一开始v[j] = 0 因为v[j]是根
v[p] = x[i] - x[p]
v[q] = x[j] - x[q]
x = x[q] - x[p]
得到
v[j] = v[p] - x - v[q]
同理可得当 j 是大树的情况
#Code
#include <iostream>
#include <cstdio>
using namespace std;
const int MAXL = 50000;
int ans;
int n, m;
int id[MAXL], sz[MAXL], v[MAXL]; //v[i] = x[id[i]] - x[i]
void init(int n)
{
for (int i = 0; i < n; i++) {
id[i] = i;
sz[i] = 1;
v[i] = 0;
}
}
int find(int p)
{
if (p != id[p]) {
int t = id[p];
id[p] = find(id[p]);
v[p] += v[t];
}
return id[p];
}
void unite(int p, int q, int x)
{
int i = find(p), j = find(q);
if (i == j) {
if (v[p] - v[q] != x) {
ans++;
}
return;
}
if (sz[i] > sz[j]) {
id[j] = i;
sz[i] += sz[j];
v[j] = v[p] - x - v[q];
} else
{
id[i] = j;
sz[j] += sz[i];
v[i] = v[q] + x - v[p];
}
}
void solve()
{
ans = 0;
init(n);
while (m--) {
int a, b, x;
scanf("%d%d%d", &a, &b, &x);
a--;
b--;
unite(a, b, x);
}
printf("%d\n", ans);
}
int main()
{
// freopen("in.txt", "r", stdin);
while (scanf("%d%d", &n, &m) != EOF) {
solve();
}
}
本文介绍了一种使用带权并查集算法解决体育馆座位分配问题的方法。在一个有N个人和M组座位需求的场景下,每组需求包含两个人A和B以及他们之间的座位间隔X。算法通过维护每个人的座位位置关系,判断是否存在矛盾的需求,并最终统计出矛盾需求的数量。
1万+

被折叠的 条评论
为什么被折叠?



