算法:食物链

思路

  • 1.

  • 2.图.

    • 1.当某点到根节点距离为1

      • 根节点吃该生物

    • 2.当某点到根节点距离为2

      • 距离为1的点吃距离为2的点

    • 3.当某点到根节点距离为3

      • 距离为2的点吃根节点

    • 插入点到根节点的距离X

      • X%3==0 该点是根节点

      • X%3==1 该点吃根节点

      • X%3==2 该点被根节点吃

    • 距离

      • 设X是第0代

      • Y吃X 则 Y为第一代

      • Z吃Y 则Z为第二代

      • Q吃Z 则Q为第三代

      • W吃Q 则W为第四代

      • .....

      • M吃N 则M为第n代

  • 3.并查集

    • 维护

      • 操作前每个点指向其父节点

      • 每个点到其父节点距离为1

      • 在进行路径压缩后每个点直接指向根节点

      • 我们在进行路径压缩的同时更新每个点到根节点的距离

  • 变量说明(服务下if说明)

    • t 说法类型

      • 1.x 和y是同类

      • 2.x吃y

    • x 动物的编号

    • y 动物的编号

    • n 动物的数量

    • 不符号条件的加粗

  • if的分类说明

    • 1.当输入的编号x/y>n时

      • if(x>n||y>n)

    • 2.当输入的编x/y<=n时

      • else

    • 2.1当t==1

      • if(t==1)

    • 2.2当t==2

      • else

    • 2.1.1当px == py && d[x] % 3 != d[y] % 3

      • if(px == py && d[x] % 3 != d[y] % 3)

    • 2.1.2当px!=py

      • else if(px!=py)

    • 2.2.1 当px == py && (d[x] - d[y] - 1) % 3

      • if (px == py && (d[x] - d[y] - 1) % 3)

    • 2.2.2当px!=py

      • else if (px != py)

#include<iostream>
using namespace std;
const int N = 5e4 + 10;
int n,m;//n为动物数,m为语句数
int p[N],d[N];
//p是 父 d是距离
/*
并查集
1.子指向自己的父
在使用find一次后全部指向根节点
查找结点,由子节点开始向父-->爷-->根
p[i]存放i的父结点的下标更新后存放i的根结点的下标
d[i]存放i到父的层数,find一次后更新为到根的层数
只有根节点自己等于自己
*/
int find(int x)
{
	if (p[x] != x) 
	{
		
		/*
		* 因为find是递归函数,本题中的find出的时候代表p[x]=x,则这时候每出一层
		* 会将一个p[]结点赋值为其根结点
		find(p[x])用作子节点走向根节点
		return p[x]用于取得根节点后,给为找到根结点而走过的子孙结点赋值为根结点的值
	    p[x]=find(p[x])当赋值时会因为递归导致这时候的find(p[x])是找到根节点后的,
		因此存的都是根节点赋值赋入的都是根节点,即此时的p[x]存的是根节点
		概况为:p[x]=find(p[x]) 后p[x]存的都是根节点
	    但
		  d[]的目标d[子到父]+d[父到爷]+d[爷到祖爷]+..+d[..到根]
		  我们要找的是每一步到每一步的的父节点的长度
		*/
		int t = find(p[x]);
		d[x] += d[p[x]];//这时候我们不调用find赋值根结点每一个不赋值不改变
		p[x] = t;
		//子存父 子存爷 子存祖爷..子存根
		//p[x]=findp[x]//将父系列赋给子逐层增加
		//d[x]+=d[p[x]]加这时候父结点存储的到父的父结点的位置
	}
	
	return p[x];
}

int main() 
{
	int res;//记录假话数
	int t, x, y;//z选项 x y
	scanf("%d%d",&n,&m);
	for (int i = 1; i <= n; i++) p[i] = i;//初始化
	while (m--) 
	{
		scanf("%d%d%d", &t, &x, &y);
		if (x > n || y > n)res++;
		else
		{
			int px=find(x),py=find(y);
			//find()=更新每棵树的结点存储的信息(更新为根节点)
			
			if (t==1)
			{
				//p[x]=p[y]代表此时根是同一个,因为赋值是自下而上 最终根可能值同,但是p[]不同
				//if(px==py&&d[x] % 3 != d[y] % 3 )res++;
				//d[x] % 3 != d[y] % 3 -->(d[x]-d[y])%3 !=0 --> (d[x]-d[y])%3 =1/2=true   
				//if (px == py && d[x] % 3 != d[y] % 3)res++;

				/*补充针对(d[x] - d[y]) % 3  不能换为 d[x] % 3 != d[y] % 3
				如果d[x]和d[y]一正一负的话!=的判断方法就是错误的,比如说d[x] = -1,d[y] = 2,
				两者都是根节点的猎物,(d[x] - d[y]) % 3 == 0, 但是d[x] % 3 = -1, d[y] % 3 = 2,
				如果实在想判断两者余数相等,可以改成(d[x] % 3 + 3) % 3 != (d[y] % 3 + 3) % 3,
				这样就消除了负数的影响。这个问题归根结底是计算机取模计算余数是没有正负限制的,
				而我们数学一般规定余数为非负数。
				*/
				if (px == py && (d[x] - d[y]) % 3) res++;
				else if (px != py) 
				{
					/* xy不在一个集合里, 先进行集合拼接在进行判断
					   p[x]=find(p[y]) 让x指向y
					   此时因为d[p[x]]的长度我们并不知道,因此需要算
					   我们需要满足的条件 x y是同类
					   1. d[x新]=d[x]+?  d[y]=d[y]
					   2. d[x新]%3=d[y]%3 同时去取余
					      d[x]+?=d[y]
						  ?=d[y]-d[x]
					*/
					 p[px] = py;//y的根值赋给x 因为这时候p[]存的全是根值
					//p[find(x)]=find(y);等同于
					d[px] = d[y] - d[x];//更新原x的根到y的根的距离,因为存的就是当前点到根的距离
				}else
				{
					/*
			          d[x] % 3 == 0;根节点
			          d[x] % 3 == 1;吃根节点
			          d[x] % 3 == 2;被根节点吃
			          A-->B A到B的肚子里
			          t=d[x]%3
			          0-->1
			          1-->2
			          2-->0
			          条件:x吃y
			          y-->x
			          0-->1
			          1-->2
			          2-->0
			          即为%3前x比y大1
					  正确的d[x]-d[y]=1
					       (d[x]-d[y]-1)%3=0
                      不正确的(d[x]-d[y]-1)%3=1
			          */
					if (px == py && (d[x] - d[y] - 1) % 3)res++;
					else if (px != py) 
					{
					/*
					 1. d[x新]=d[x]+?  d[y]=d[y]
					 2. d[x新]-d[y]=1
					    d[x]+?-d[y]=1
						  ?=1+d[y]-d[x]
					*/ 
						p[px] = py;//拼接
						d[px] = 1 + d[y] - d[x];
					}
				}
			}
		}
	}
	printf("%d\n",res);
	return 0;
}
//------------------------------------------------
#include<iostream>
using namespace std;
const int N = 5e4 + 10;
int n, m;
int p[N], d[N];
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()
{
	int res = 0;
	int t, x, y;
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) p[i] = i;
	while (m--)
	{
		scanf("%d%d%d", &t, &x, &y);
		if (x > n || y > n) res++;
		else
		{
			int px = find(x), py = find(y);
			if (t == 1)
			{
				//if (px == py && d[x] % 3 != d[y] % 3) res++;
				if (px == py && (d[x] - d[y]) % 3) res++;
				else if (px != py)
				{
					p[px] = py;
					d[px] = d[y] - d[x];
				}
			}
			else
			{
				if (px == py && (d[x] - d[y] - 1) % 3) res++;
				else if (px != py)
				{
					p[px] = py;
					d[px] = 1 + d[y] - d[x];
				}
			}
		}
	}
	printf("%d\n", res);
	return 0;
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只符华单推人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值