食物链

34 篇文章 2 订阅
22 篇文章 1 订阅

1182:食物链

总时间限制: 

1000ms

内存限制: 

65536kB

描述

  动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。
现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这N个动物所构成的食物链关系进行描述:
第一种说法是"1 X Y",表示X和Y是同类。
第二种说法是"2 X Y",表示X吃Y。
此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
(1) 当前的话与前面的某些真的话冲突,就是假话;
(2) 当前的话中X或Y比N大,就是假话;
(3) 当前的话表示X吃X,就是假话。
你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。

输入

第一行是两个整数N和K,以一个空格分隔。
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
若D=1,则表示X和Y是同类。
若D=2,则表示X吃Y。

输出

只有一个整数,表示假话的数目。

样例输入

100 7
1 101 1 
2 1 2
2 2 3 
2 3 3 
1 1 3 
2 3 1 
1 5 5

样例输出

3

来源

Noi 01

  这道题不难看出是一道用并查集来解决的题目,是相当难的,在洛谷上面是一道提高+/省选-的题目,给大家讲一下这道题的思路吧。

思路:

  刚开始,输入了n和k,然后输入3个整数,判断第一个整数是1还是2,如果是1的话,首先要判断是不是假的,怎么判断这道题是不是假的呢?第一个判断条件是如果输入的后两个数b和c其中任意一个大于了n的话,就是假话,毕竟动物编号最大才n,你大于n了,就根本没有这个动物了,第二个判断条件就是是否冲突,怎么查是否冲突呢?在这道题里面冲突的意思是,b已经可以吃c了,你把b和c放到同一类,这就明显是假话了,那这该怎么判断呢?我们可以在判断第一个数为2之后,确认了这句话不是假话,就可以进行unionSet操作,但是由于他们是b吃y,所以我们原来的unionSet函数不能实现这个要求,我们就可以重新写一个函数unionSetEat,并且多加一个结构数组*eat,里面存的是b可以吃的动物的编号,就这样,可以判断是否冲突,只需要判断eat[b的下标]是不是等于c的下标,如果是,代表之前是b吃c的,而现在竟然说b和c是同类,这明显是假话,如果以上两种都不是的话,那么就用普通的unionSet函数进行合并,表示两个动物是同一个类别。

  如果输入的第一个数是2的话,先判断b是不是等于c,如果是,就代表自己吃自己,不是搞笑的吗,所以相等的话就是假话,然后判断b和c是不是同一个集合里面的(是不是同一类),如果是,就代表冲突,毕竟都是一类了,还互相吃是不可能的(根据题意),最后在判断一下,eat[c的下标]是不是等于b的下标,这个的意思是,你输入的两个数b和c代表b可以吃c,而这个判断时判断c是不是要吃b,如果是,那岂不是要互吃了,所以这句话也是假的,以上3种情况都不符合的话,那就代表说的是真话了,就运用新的函数unionSetEat函数,给两个动物做一个标记。

  最后,如果输入的是假话,那么就进行sum++,最后输出sum就行了。

食物链:

#include<bits/stdc++.h>
using namespace std;
template<class T>
struct DisjointSet{
	int *parent;
	int *eat;
	T *data;
	map<T,int> m;
	int capacity;
	int size;
	DisjointSet(int max=1000){
		capacity=max;
		size=0;
		parent=new int[max+1];
		data=new T[max+1];
		eat=new T[max+1];
	}
	~DisjointSet(){
		delete [] parent;
		delete [] data;
	}
	bool insert(T x){
		if(size==capacity) return false;
		size++;
		data[size]=x;
		parent[size]=-1;
		m[x]=size;
		return true;
	}
	int getIndex(T x){
		for(int i=1;i<=size;i++)
		  if(data[i]==x)
		    return i;
		return -1; 
	}
	int find(T x){
		typename map<T,int>::iterator it;
		it=m.find(x);
		if(it==m.end()) return -1;
		int i,rt;
		i=rt=it->second;
		while(parent[rt]>0)
		  rt=parent[rt];
		return rt;
	}
	void unionSet(T x,T y){
		int rx,ry;
		rx=find(x);
		ry=find(y);
		if(rx==-1||ry==-1) return ;
		if(rx==ry) return ;
		if(parent[rx]<parent[ry]){
			parent[rx]+=parent[ry];
			parent[ry]=rx;
		}
		else{
			parent[ry]+=parent[rx];
			parent[rx]=ry;
		}
		find(x);
		find(y);
	}
	void unionSetEat(T x,T y){
		int rx,ry;
		rx=find(x);
		ry=find(y);
		if(rx==-1||ry==-1) return ;
		if(rx==ry) return ;
		parent[ry]=rx;
	}
};
int main(){
	int n,k,sum=0;
	cin>>n>>k;
	while(k--){
		DisjointSet<int> s;
		for(int i=1;i<=n;i++) s.insert(i);
		int a,b,c;
		cin>>a>>b>>c;
		if(b>n||c>n) sum++;
		else if(a==1){
			if(s.eat[s.getIndex(b)]==s.getIndex(c)) sum++;
			else s.unionSet(b,c);
		}
		else{
			if(b==c) sum++;
			else if(s.find(b)==s.find(c)) sum++;
			else if(s.eat[s.find(c)]) sum++;
			else s.unionSet(b,c);
		}
	}
	cout<<sum<<endl;
	return 0;
} 

  今天的题目分享就到这里了,这道题的分析还是有些难度的!

该题链接1: 

OpenJudge - 1182:食物链http://bailian.openjudge.cn/practice/1182该题链接2: 

[NOI2001] 食物链 - 洛谷https://www.luogu.com.cn/problem/P2024

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

龙星尘

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

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

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

打赏作者

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

抵扣说明:

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

余额充值