POJ1182 食物链(并查集)
题意:
中文题不解释
要点:
很经典的并查集问题,也很难。网上一共两种做法:一种是算相对类别偏移量,用了一系列找规律,难的爆炸,反正我看懂了但是再做一次肯定是做不出来的;另一种是将数组开三倍,表示所有的情况,这种我还可以尝试一下。
开三倍数组存储所有情况:
要点是将P[X]开成最大值的三倍,1~n表示当前x属于A;n+1~2n表示当前x属于B,2n+1~3n表示当前x属于C,每次合并时将x属于ABC的三种情况全部合并起来,这样就可以完全考虑。最后求假话数量,只要考虑对应的x属于的集合即可。这种方法内存虽然要求高了点,但实际运行时间还是少的。而且稍微好理解一点,还是用这种方法吧。
15316600 | Seasonal | 1182 | Accepted | 1276K | 250MS | C++ | 1094B | 2016-03-26 15:50:34 |
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define maxn 50050
int p[3*maxn],rank[3*maxn];//将x对应A,x对应B,x对应C都储存下来
int m, k;
void init()
{
for (int i = 1; i <= 3 * m; ++i)
{
p[i] = i;
rank[i] = 0;
}
}
int find(int x)
{
if (p[x] == x) return x;
return p[x] = find(p[x]);
}
void merge(int x, int y)
{
x = find(x);
y = find(y);
if (x == y) return;
if (rank[x] > rank[y])
p[y] = x;
else
{
p[x] = y;
if (rank[x] == rank[y]) rank[y]++;
}
}
bool same(int x, int y)
{
return find(x) == find(y);
}
int main()
{
int d, x, y,num=0;
scanf("%d%d", &m, &k);
init();
while (k--)
{
scanf("%d%d%d", &d, &x, &y);
if (x > m || y > m || (d == 2 && x == y))
{
++num;
continue;
}
if (d == 1)
{
if (same(x, y + m) || same(x, y + 2 * m)) ++num;//当x为A时y为B或C不满足条件
else //不用考虑x为B或C的问题,因为下面已经将x=ABC三种情况全合并起来了
{ //如果出现x=A时y=B或C就说明x=B或C时也不满足条件
merge(x, y); //为A时x与y相等
merge(x + m, y + m);
merge(x + 2 * m, y + 2 * m);
}
}
if (d == 2)
{
if (same(x, y) || same(x, y + 2 * m)) ++num;//当x, y同类,或者属于A的x吃了C的y,则为假话
else
{
merge(x, y + m); //属于A的x吃了属于B的y
merge(x + m, y + 2 * m);//属于B的x吃了属于C的y
merge(x + 2 * m, y); //属于C的x吃了属于A的y
}
}
}
printf("%d\n", num);
return 0;
}
相对类别偏移量:
参考博客:点击打开链接
15316265 | Seasonal | 1182 | Accepted | 508K | 282MS | C++ | 833B | 2016-03-26 14:56:41 |
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define maxn 50050
int n, k;
int p[maxn], rank[maxn];//rank[x]用来表示x与p[x]的食物链关系
void init()
{
for (int i = 1; i <= n; ++i)
{
p[i] = i;
rank[i] = 0;
}
}
int find(int x)
{
if (p[x] != x)
{
int t = p[x];
p[x] = find(p[x]);
rank[x] = (rank[x] + rank[t]) % 3;//找规律得到
}
return p[x];
}
void merge(int x, int y,int d)
{
int xf = find(x);
int yf = find(y);
p[xf] = yf;
rank[xf] = (rank[y] - rank[x] + 2 + d) % 3;
}
int main()
{
int d, x, y,num=0;
int xf, yf;
scanf("%d%d", &n, &k);
init();
while (k--)
{
scanf("%d%d%d", &d, &x, &y);
if (x > n || y > n || (d == 2 && x == y))
num++;
else
{
xf = find(x);
yf = find(y);
if (xf == yf)//等于才能判断,不等于判断不了可以直接合并
{
if ((rank[x] - rank[y] + 3) % 3 != d - 1)
num++;
}
else
merge(x, y, d);
}
}
printf("%d\n", num);
return 0;
}