洛谷 P2024 食物链

P2024 食物链

法一:种类并查集

#include<cstdio>
int fa[250000];
int n,k,ans;
//fa[i]表示与i同类的集合,fa[i+n]表示i吃的集合,fa[i+2n]表示吃i的集合
int find(int x)
{
	if(fa[x]!=x)	fa[x]=find(fa[x]);
	return fa[x];
}
//int union1(int x,int y)
//{
//	x=find(x);
//	y=find(y);
//	fa[x]=y;
//}//直接写进主程序 
int main()
{
	int i,a,b,c,f1,f2,f3,f4;
	scanf("%d%d",&n,&k);
	for(i=1;i<=3*n;i++)
		fa[i]=i;
	for(i=1;i<=k;i++)
	{
		scanf("%d%d%d",&a,&b,&c);
		if(b>n||c>n||(a==2&&b==c))
			ans++;
		else
		{
			if(a==1)
			{
				f1=find(b);
				f2=find(c);
				f3=find(c+n);
				f4=find(c+2*n);
				if(f1==f3||f1==f4)//如果已知b被c吃或b吃c则为假话
					ans++;
				else
					if(f1!=f2)
					{
						fa[f1]=f2;//与b同类的集合和与c同类的集合合并
						fa[find(b+n)]=f3;//b吃的集合和c吃的集合合并
						fa[find(b+2*n)]=f4;//吃b的集合和吃c的集合合并
					}
			}
			if(a==2)
			{
				f1=find(b);
				f2=find(c);
				f3=find(c+n);
				f4=find(c+2*n);
				if(f1==f2||f1==f3)//如果已知b和c同类或b被c吃则为假话
					ans++;
				else
					if(f1!=f4)
					{
						fa[find(b+n)]=f2;//b吃的集合和与c同类的集合合并
						fa[find(c+2*n)]=f1;//吃c的集合和与b同类的集合合并
						fa[find(b+2*n)]=f3;//吃b的集合和c吃的集合合并 
						//!!!上面这种非常容易忘,当心了 
					} 
			}
		}
	}
	printf("%d",ans); 
	return 0;
}

法二:加权并查集

加权并查集的做法并非将同类放入同一个并查集,而是将所有有关系的动物都放入同一个并查集。这一点跟上面的做法不同。
r[i]表示i结点与父亲结点的关系,r[i]=0 表示father[i]与i同类;1表示father[i]吃i;2表示i吃father[i]。
在这道题中,处理合并与路径压缩时的权值变化的计算方法基本上都由枚举、找规律得出。事实上,即使没有规律,或许也可以通过直接写大量if判断语句来处理权值变化。处理权值变化的方法需要具体情况具体分析。

#include<cstdio>
#include<algorithm>
using namespace std;
int father[60000];
int r[60000];
int n,k;
int find(int x)
{
    if(x!=father[x])
    {
        int xx=father[x];
        father[x]=find(father[x]);
        r[x]=(r[x]+r[xx])%3;//见“解释4”
    }
    return father[x];
}
int ans;
int main()
{
    int a,i,b,c,fb,fc;
    scanf("%d%d",&n,&k);
    for(i=1;i<=n;i++)
        father[i]=i;
    for(i=1;i<=k;i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        if(b>n||c>n||(b==c&&a==2))//第二和第三条判定假话的方法
            ans++;
        else
        {
            fb=find(b);
            fc=find(c);
            if(a==1)
            {
                if(fb==fc)//b和c在相同集合中,即能确定b和c有关系
                {
                    if(r[b]!=r[c])//在find中已经完成路径压缩,因此r[b]、r[c]表示它们与同一个结点的关系
                        ans++;//如果它们与该结点关系相同,显然b、c同类,不矛盾,反之则矛盾,答案加1
                }
                else//如果不能确定b、c有关系
                {
                    father[fc]=fb;//合并b、c所在集合
                    r[fc]=(r[b]-r[c]+3)%3;//这个不好解释,见下方“解释1”
                }
            }
            else//如果输入是b吃c
            {
                if(fb==fc)//如果可以确定b与c的关系
                {
                    if((r[b]+1)%3!=r[c])//如果b不是吃c的(原因见“解释2”)
                        ans++;//答案加1
                }
                else
                {
                    father[fc]=fb;//如果不能确定b与c关系,则合并b和c所在集合
                    r[fc]=(r[b]-r[c]+4)%3;//见“解释3”
                }
            }
        }
    }
    printf("%d",ans);
    return 0;
}

解释1:
可以知道此时b和c是同类,并且b和c所在的集合都已经完成了路径压缩,也就是b和c此时的父结点分别就是fb和fc。直接枚举r[b]、r[c]值,推出对应r[fc]值,找规律即可。
举例:r[b]=1,r[c]=2时,fb吃b,c吃fc,b和c同类,可得fc吃fb,即r[fc]=2。
很容易发现r[fc]=(r[b]-r[c]+3)%3。
解释2:
b和c所在的集合都已经完成了路径压缩,且fb==fc,也就是b和c此时的父结点为同一个即fb。枚举r[b]值推出r[c]可找出规律。
举例:r[b]=1,那么b被fb吃,而如果b吃c,显然c吃fb,因此r[c]=2。
很容易发现当且仅当(r[b]+1)%3==r[c]时b是吃c的。
解释3:
可以知道此时b吃c,并且b和c所在的集合都已经完成了路径压缩,也就是b和c此时的父结点分别就是fb和fc。仍然是枚举r[b]、r[c]值推倒r[fc]值找规律。
举例:r[b]=1,r[c]=0,那么b被fb吃,而b吃c,因此c吃fb,而c与fc同类,因此fc吃fb,因此r[fc]=2。
很容易发现r[fc]=(r[b]-r[c]+4)%3。
解释4:
如果某点与其父结点关系为a,其父结点与根结点关系为b,那么可以知道该点与根结点关系为(a+b)%3。
举例:某点被父结点吃,即r[点]=1,父结点吃根结点,即r[father[点]]=2,那么显然该点与根结点同种类,即路径压缩后r[点]=(1+2)%3=0。
(这个不好证也没必要证,多列几组试一下就知道了)

推荐优秀讲解:

食物链
食物链

题目描述似乎缺失了关键信息,通常我会需要了解“P10780 食物”是什么具体的算法竞赛题目,它来自在线平台(Luogu),以及该题目的大致背景、条件和目标。食物(Food)可能是某种数据结构或算法问题,比如贪吃蛇、分配任务等。 然而,我可以给你提供一个通用的模板: **[ P10780 食物 - 题目解析]** 题目名称:P10780 食物(假设是关于食物分配或者饥饿游戏的问题) 链接:[插入实际题目链接] **背景:** 此题通常涉及动态规划或者搜索策略。场景可能是有n个参与者(选手或角色),每个都有特定的食物需求或者优先级,我们需要在有限的食物资源下合理分配。 **分析:** 1. **输入理解**:首先读入n个参与者的信息,包括每个人的需求量或优先级。 2. **状态定义**:可以定义dp[i][j]表示前i个人分配完成后剩余的食物能满足第j个人的最大程度。 3. **状态转移**:递推式可能涉及到选择当前人分配最多食物的版本,然后更新剩余的食物数。 4. **边界条件**:如果剩余食物不足以满足某人的需求,则考虑无法分配给他;如果没有食物,状态值设为0。 5. **优化策略**:可能需要对状态数组进行滚动更新,以减少空间复杂度。 **代码示例(伪代码或部分关键代码片段):** ```python # 假设函数分配_food(demand, remaining)计算分配给一个人后剩余的食物 def solve(foods): dp = [[0 for _ in range(max_demand + 1)] for _ in range(n)] dp = foods[:] # 从第一个到最后一个参与者处理 for i in range(1, n): for j in range(1, max_demand + 1): if dp[i-1][j] > 0: dp[i][j] = max(dp[i][j], dp[i-1][j] - foods[i]) dp[i][j] = max(dp[i][j], distribute_food_to(i, dp[i-1][j])) return dp[n-1][max_demand] ``` **相关问题--:** 1. 这道题是如何运用动态规划的? 2. 如果有优先级限制,应该如何调整代码? 3. 怎样设计搜索策略来解决类似问题?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值