您的位置:首页 > 其它

POJ 1182 食物链(向量偏移解法)——及相同解法一道例题

2015-04-09 12:27 274 查看
此题是并查集的一道经典题目,解法并不唯一,此篇文章给出的是本题的向量偏移解法,另外一种分组解法会在我的同目录下的另一篇文章里给出,同样的,也会给出另一个例题。

题目链接:http://poj.org/problem?id=1182

题目大意是说,一共有三类物种,分别是A,B,C,它们互相制约形成一条食物链(环)。例如A吃B,B吃C,C吃A。现在,告诉你一共有n个动物,m个限制条件——形如d, x, y,当d = 1时,表示x和y属于同类物种(但并不知道是A,B,C的哪一种);当d = 2时,表示x可以吃y。

但是给你的m个条件中,有一部分是假话,假话的定义为,与之前的真话相矛盾,或者不符合题意(具体见题目描述)。

要求统计并输出假话的数目。

向量偏移解法:我们除了father数组外,还需要开一个offset——偏移数组。之所以称作向量偏移法,是因为我们把father[x] = y看作是x -> y的一条向量,并且我们将最初的find查集函数和union并集函数都做了一些改变,在并查的过程中,我们我们将需要的值传递下去。例如两个集合a->b, c->d,当我们把a和d链接到一起的时候,首先找到了a的父亲b,d的父亲d本身,并且把a和d的关系更新到offset[d]上,但此时offset[c]尚未更新,但当下一次再使用查集函数找c的时候,我们会先找到它的父亲d,并回溯更新offset[c],以此来完成"向量"在内容上的偏移。

对于向量偏移解法解决分组问题,我们假设组数为DEPTH(此题DEPTH=3),那么我们将所有offset[]中的内容对DEPTH取模即可得到相应的组号。

AC代码:

#include <iostream>
#include <cstdio>
using namespace std;
int father[100010], offset[100010];
#define DEPTH 3
int n, k, ans;
int findset(int x) {
int tmp;
if(father[x] != x) tmp = findset(father[x]);
else return x;
offset[x] = (offset[x] + offset[father[x]]) % DEPTH;
father[x] = tmp;
return tmp;
}
void init() {
ans = 0;
for(int i = 1; i <= n; i++) {
father[i] = i;
offset[i] = 0;
}
}
void unionset(int x, int y, int d) {
int fx, fy;
fx = findset(x);
fy = findset(y);
if(fx == fy) {
int tmp = (DEPTH + offset[y] - offset[x]) % DEPTH;
if(tmp != d) ans++;
return ;
}
father[fy] = fx;
offset[fy] = (offset[x] - offset[y] + d + DEPTH) % DEPTH;
}
int main() {
scanf("%d%d", &n, &k);
init();
int d, x, y;
for(int i = 0; i < k; i++) {
scanf("%d%d%d", &d, &x, &y);
if(x > n || y > n || (x == y && d == 2)) ans++;
else unionset(x, y, d - 1);
}
printf("%d\n", ans);
return 0;
}


同样适用向量偏移解法的不错的题目还有哈理工oj上的一道名为"土豪的时代"的题:

题目链接:http://acm.hrbust.edu.cn/index.php?m=ProblemSet&a=showProblem&problem_id=2240

土豪的时代
Time Limit: 500 MSMemory Limit: 32768 K
Total Submit: 80(24 users)Total Accepted: 23(20 users)Rating:



Special Judge: No
Description
土豪圈有一个习惯:从来不告诉别人自己到底有多少钱。但他们总是喜欢和其他土豪比较,来看看谁更土豪。于是每每几天,就会爆出一些关于土豪资产的消息,比如A土豪比B土豪多了3254万,C土豪比D土豪少2124万等等,从这些消息中也很容易推测出某两个土豪之间的资产关系。小破觉得和土豪交朋友是件非常愉快的事,但她想知道,某两个土豪,哪个更土豪。你能帮帮她么。
Input
多组测试数据

每组测试数据第一行有一个数n。( n<=100000 )

接下来有n行,每行包含一种操作。

操作分为两种:

1 a b m :表示a比b多m万元。

2 a b: 表示询问a比b多多少万元。

(0 < a,b < n)

(0 <= m <= 1000)

评测数据保证合法。

Output
对于每次的询问操作,输出其结果。当结果未知时,输出"?"。
Sample Input
5

1 1 2 10

2 1 3

1 3 4 10

1 2 3 10

2 4 1
Sample Output
?

-30
Source
哈尔滨理工大学第五届ACM程序设计竞赛(热身)
题目大意:告诉你了编号为a的土豪比编号为b的土豪多x万元,边给定条件边查询某两个人的资产是否可比,如果可比,输出a比b多多少万元。由于此题是边给定条件边查询,所以我们不好通过离线建图然后通过搜索处理。于是我们可以用向量偏移的并查集方式来处理此题。因为此题需要的是土豪之间的比较关系,我们可以假定其中一个土豪的资产为0万元,offset数组记录每个土豪的相对资产即可。

【这里由于本题不再是分组问题,所以无需DEPTH变量取模等操作,而且m最大值不超过1000,无须担心数值过大超界的问题】

AC代码:
#include <iostream>
#include <cstdio>
using namespace std;
int father[100010], offset[100010];
int n, k;
int findset(int x) {
int tmp;
if(father[x] != x) tmp = findset(father[x]);
else return x;
offset[x] = offset[x] + offset[father[x]];
father[x] = tmp;
return tmp;
}
void init() {
for(int i = 0; i <= n; i++) {
father[i] = i;
offset[i] = 0;
}
}
void unionset(int x, int y, int d) {
int fx, fy;
fx = findset(x);
fy = findset(y);
if(fx != fy) {
father[fy] = fx;
offset[fy] = offset[x] - offset[y] + d;
}
}
int main() {
while(~scanf("%d", &n)) {
init();
int ret, a, b, m;
while(n--) {
scanf("%d", &ret);
if(ret == 1) {
scanf("%d%d%d", &a, &b, &m);
unionset(a, b, m);
} else {
scanf("%d%d", &a, &b);
int fx, fy;
fx = findset(a);
fy = findset(b);
if(fx == fy) printf("%d\n", offset[b] - offset[a]);
else puts("?");
}
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: