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;
}
浙公网安备 33010602011771号