HDU 1182---食物链

POJ  1182   食物链问题(石头剪刀布)-----统计假话的数量

#include<iostream>
#include<stdio.h>
#define M 50002
#define N 100003
using namespace std;
int father[M],rank[M],num,total;

int find(int x)
{
	int a=father[x];
	if(x==a) return a;
	father[x]=find(father[x]);
	rank[x]=(rank[x]+rank[a])%3;
	return father[x];
}

/*bool UnionSet(int x,int y,int d)
{
	int a=find(x);
	int b=find(y);
	if(a==b) 
	{
	if(d==(rank[y]+3-rank[x])%3)
	{
	return true;
	}
	else 
	{
	return false; //命题为假
}
	}
father[b]=a;
	rank[b]=(rank[x]+d+3-rank[y])%3; //rank[b]记录的是b和a之间的关系
return true;
}

void istrue(int m)
{
	int t,x,y;
 for(int i=0;i<m;i++)
 {
	cin>>t>>x>>y;
	if((x>total)||(y>total)||((t==2)&&(x==y)))
	{
	num++;
	continue;
	}
	if (!UnionSet(x,y,t-1))
	{
 num++;
	}
 }
}*/
void UnionSet(int x,int y,int d)
{
	int a=find(x);
	int b=find(y);
	if(a==b) return ;
	father[b]=a;
	rank[b]=(rank[x]+d+3-rank[y])%3;
}
int istrue(int x,int y,int t)
{
	if((x>total)||(y>total)||((t==2)&&(x==y))) return 0;
	int a=find(x);
	int b=find(y);
	if(a!=b) return 1;
	if((t-1)==(rank[y]+3-rank[x])%3) return 1;
	else return 0;
}
int main()
{
	int n,m,x,y,d;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
	rank[i]=0;
	father[i]=i;
	} 
 total=n; //记录动物的数量
num=0; //记录假话的数量
//istrue(m);
	for(int i=0;i<m;i++)
	{
	scanf("%d%d%d",&d,&x,&y);
	if(!istrue(x,y,d)) num++;
	else UnionSet(x,y,d-1);
	}
	printf("%d\n",num);
	return 0;
}

 

 

分析: 这题最主要的就是弄明白两个公式(关于每个生物属于哪个分类)

第一: 在查找当前节点的父节点时,更新了一次当前节点的等级分类  rank[x]=(rank[x]+rank[a])%3;

注:(前提条件是当前节点不是根节点,即该节点有前驱)

(1)若当前节点的父节点就是根节点,则rank[a]为0,上式没有改变当前节点的等级;

(2)若当前节点的父节点不是根节点,则使用find函数会将当前节点的父节点进行更新,此时也必须将rank进行更新,因为rank记录的是当前节点与其父节点间的关系;

现在的rank即是  “儿子”与“爷爷”  间的关系(已知儿子与父亲间的关系rank[x],并且已知父亲与爷爷间的关系rank[a],求儿子与爷爷间的关系(即最新的rank[x]))

总共的分类只有3中(若a吃b,b吃c;则必有c吃a(构成一个环,形成食物链));0---同类;1----儿子吃父亲;2---儿子被父亲吃;

 

第二:在合并两个节点时,使用了公式:  rank[b]=(rank[x]+d+3-rank[y])%3

     其中,d---x与y的关系;  a----x的父亲;  b----y的父亲;

这里  已知x与y的关系d(1---x吃y;2---x被y吃;0---x与y是同类);  x与a的关系rank[x] ; y与b的关系rank[y] ;  求b与a的关系rank[b](这里记a是b的父亲)

 

 关键就是以上这两点,最初我没有好好理解的是在find函数中,也是要对rank进行改进的,因为只要某节点的祖先变化,rank就要跟着进行更新;

 新的父亲就是原来的爷爷;

   第二点就是已知两节点(x、y)间的关系d;他们分别与他们的祖先间的关系rank[x]、rank[y];

求两祖先间的关系,可以自己推下!

 

 

 

 

 

 

版本2

 

#include<iostream>
#include<stdio.h>
#define M 50002
#define N 100003
using namespace std;
int father[M],rank[M],num,total;

int find(int x)
{
	int a=father[x];
	if(x==a) return a;
	father[x]=find(father[x]);
	rank[x]=(rank[x]+rank[a])%3;
	return father[x];
}

bool UnionSet(int x,int y,int d)
{
	int a=find(x);
	int b=find(y);
	if(a==b) 
	{
	if(d==(rank[y]+3-rank[x])%3) return 1;
	else return 0; //命题为假
}
father[b]=a;
	rank[b]=(rank[x]+d+3-rank[y])%3; //rank[b]记录的是b和a之间的关系
return 1;
}

void istrue(int m)
{
	int t,x,y;
 for(int i=0;i<m;i++)
 {
	scanf("%d%d%d",&t,&x,&y);
	if((x>total)||(y>total)||((t==2)&&(x==y)))
	{
	num++;
	continue;
	}
	int l=UnionSet(x,y,t-1); //不能写成 if(!UnionSet(x,y,t-1)) num++;这样写就一直wrong
	if (!l) num++; //这两句不能合并,合并写就出错,我也不知道为什么
}
}
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
	rank[i]=0;
	father[i]=i;
	} 
 total=n; //记录动物的数量
num=0; //记录假话的数量
istrue(m);
	printf("%d\n",num);
	return 0;
}

 

posted @ 2014-04-26 11:05  zhoudan  阅读(139)  评论(0)    收藏  举报