食物链 POJ - 1182 种类并查集或带权并查集

题目链接:https://vjudge.net/problem/POJ-1182
错误代码
用并查集表示是同类,vector保存关系。查询时用的是根节点的关系,由于合并时没有把子节点上的关系转移到根节点导致关系缺失。

#include <cstdio>
#include <algorithm>
#include <queue>
#include <vector>
#include <cstring>
#include <iostream>
#include <cmath>
#include <map>
#include <list>
#include <stack>
using namespace std;
int pa[50005];
vector <int> g[50005];
int fnd(int x)
{
    return x==pa[x]?x:pa[x]=fnd(pa[x]);
}
int check(int a,int b)
{
    int la=g[a].size(),lb=g[b].size();
    for(int i=0; i<la; i++)
    {
        if(g[a][i]==b)
            return 1;
    }
    for(int i=0; i<lb; i++)
    {
        if(g[b][i]==a)
            return 2;
    }
    return 0;
}
int main()
{
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i=1; i<=n; i++)
    {
        pa[i]=i;
    }
    int cnt=0;
    for(int i=0; i<k; i++)
    {
        int d,x,y;
        scanf("%d%d%d",&d,&x,&y);
        if(x>n||y>n)
        {
            cnt++;
            continue;
        }
        int a=fnd(x),b=fnd(y);
        if(d==1)
        {
            if(check(a,b)>0)
                cnt++;
            else
            {
                pa[a]=b;
            }
        }
        else
        {
            if(a==b)
                cnt++;
            else if(check(a,b)==2)
                cnt++;
            else
                g[a].push_back(b);
        }
    }
    printf("%d\n",cnt);
    return 0;
}

种类并查集
转自:https://www.cnblogs.com/fly-white/p/10092762.html
同一个集合表示同一种动物。每一种动物有3个节点 : x,x+n,x+2 * n,x表示自己,x+n表示其天敌,x+2 * n表示天敌的天敌。特别注意x是x+2 * n的天敌。每次合并时都要维护三个点,这样关系就不会缺失了。

#include <cstdio>
#include <algorithm>
#include <queue>
#include <vector>
#include <cstring>
#include <iostream>
#include <cmath>
#include <map>
#include <list>
#include <stack>
using namespace std;
int pa[150005];
int fnd(int x)
{
    return x==pa[x]?x:pa[x]=fnd(pa[x]);
}
void merge1(int x,int y)
{
    x=fnd(x),y=fnd(y);
    pa[x]=y;
}
int main()
{
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i=1; i<=3*n; i++)
    {
        pa[i]=i;
    }
    int cnt=0;
    for(int i=0; i<k; i++)
    {
        int d,x,y;
        scanf("%d%d%d",&d,&x,&y);
        if(x>n||y>n)
        {
            cnt++;
            continue;
        }
        if(d==1)
        {
            if(fnd(x)==fnd(y+n)||fnd(x)==fnd(y+2*n))
            {
                cnt++;
                continue;
            }
            else
            {
                merge1(x,y);
                merge1(x+n,y+n);
                merge1(x+2*n,y+2*n);
            }
        }
        else
        {
            if(fnd(x)==fnd(y)||fnd(x)==fnd(y+2*n))
            {
                cnt++;
                continue;
            }
            else
            {
                merge1(x,y+n);
                merge1(x+n,y+2*n);
                merge1(x+2*n,y);
            }
        }
    }
    printf("%d\n",cnt);
    return 0;
}

带权并查集
转自:https://blog.csdn.net/niushuai666/article/details/6981689
只要两点有关系,无论是同类还是吃与被吃,都合并到一个集合中。 定义p[a].rela表示a的pre相对于节点a的偏移量: 0 同类 1 根节点吃a 2 a吃根节点 op-1即关系。
设px为x的父节点,ppx为px的父节点,已知px与x的rela和px与ppx的rela,就可以得到ppx与x的rela(一个个列出来即可得到规律),即ppx相对于x的rela =(px相对于x的rela+ppx相对于px的rela)%3。假设有一条链的集合,1-2-3-4-5,5为根节点,更新时,会先更新4和5的rela,然后用4作为px更新3,在用3作为px更新2……。
合并时,因为节点的rela都是相对于根节点的,因此也要用根节点进行合并。 用类似于向量的思想 ,例如1->2,3->4,13为根节点,现在给出2与4的关系,可以把3号节点作为1的子节点进行合并,那么1对3的rela由1->2+2->4+4->3得到,即(p[2].rela+(op-1)-p[4].rela+3)%3,+3防止出现负数,4->3的rela就是3对4的rela的相反数。
对于题目,如果输入的两点没有关系,可以直接合并。如果有关系,需要判断。1.如果根节点对两点的rela不相等,那么它们一定不是同类 2.如果x可以吃y,设根节点为z,那么(x->z+z->y)%3一定等于op-1即1 一定要注意方向

#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<map>
using namespace std;
struct node
{
    int pre,rela;
} p[50010];
int find(int x)
{
    if(x==p[x].pre)
        return x;
    int t=p[x].pre;
    p[x].pre=find(p[x].pre);
    p[x].rela=(p[x].rela+p[t].rela)%3;
    return p[x].pre;
}
int main()
{
    int n,k;
    int op,a,b,root1,root2;
    int sum=0;
    scanf("%d%d",&n,&k);
    for(int i=1; i<=n; i++)
    {
        p[i].pre=i;
        p[i].rela=0;
    }
    for(int i=0; i<k; i++)
    {
        scanf("%d%d%d",&op,&a,&b);
        if(a>n||b>n)
        {
            sum++;
            continue;
        }
        if(op==2&&a==b)
        {
            sum++;
            continue;
        }
        root1=find(a);
        root2=find(b);
        if(root1!=root2)//两节点没有关系,可以直接合并
        {
            p[root2].pre=root1;
            p[root2].rela=(3+p[a].rela+(op-1)-p[b].rela)%3;
        }
        else//两节点有关系,需要判断
        {
            if(op==1&&p[a].rela!=p[b].rela)//两点相对于根节点的rela不相等 就一定不是同类
            {
                sum++;
                continue;
            }
            if(op==2&&((3-p[a].rela+p[b].rela)%3!=op-1))
            {
                sum++;
                continue;
            }
        }
    }
    printf("%d\n",sum);
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用 JavaScript 编写的记忆游戏(附源代码)   项目:JavaScript 记忆游戏(附源代码) 记忆检查游戏是一个使用 HTML5、CSS 和 JavaScript 开发的简单项目。这个游戏是关于测试你的短期 记忆技能。玩这个游戏 时,一系列图像会出现在一个盒子形状的区域中 。玩家必须找到两个相同的图像并单击它们以使它们消失。 如何运行游戏? 记忆游戏项目仅包含 HTML、CSS 和 JavaScript。谈到此游戏的功能,用户必须单击两个相同的图像才能使它们消失。 点击卡片或按下键盘键,通过 2 乘 2 旋转来重建鸟儿对,并发现隐藏在下面的图像! 如果翻开的牌面相同(一对),您就赢了,并且该对牌将从游戏中消失! 否则,卡片会自动翻面朝下,您需要重新尝试! 该游戏包含大量的 javascript 以确保游戏正常运行。 如何运行该项目? 要运行此游戏,您不需要任何类型的本地服务器,但需要浏览器。我们建议您使用现代浏览器,如 Google Chrome 和 Mozilla Firefox, 以获得更好、更优化的游戏体验。要玩游戏,首先,通过单击 memorygame-index.html 文件在浏览器中打开游戏。 演示: 该项目为国外大神项目,可以作为毕业设计的项目,也可以作为大作业项目,不用担心代码重复,设计重复等,如果需要对项目进行修改,需要具备一定基础知识。 注意:如果装有360等杀毒软件,可能会出现误报的情况,源码本身并无病毒,使用源码时可以关闭360,或者添加信任。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值