nyist 207&&poj 1182 食物链(并查集)

38 篇文章 0 订阅
30 篇文章 0 订阅
这篇博客介绍了如何使用并查集解决nyist 207和poj 1182题目中的食物链问题。作者分享了初次遇到此题时的困扰,以及通过参考他人思路理解并查集核心——关系域更新的过程。文中提到,关系域的更新是解题关键,同时提到了一种不带秩的合并方法,以减少内存使用。
摘要由CSDN通过智能技术生成


食物链

时间限制: 1000  ms  |  内存限制: 65535  KB
难度: 5
描述
动物王国中有三类动物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
来源
POJ
上传者
iphxer

这道题可以算的上是很经典的并查集的题目了,刚开始做的时候也没思路啊,各种wa啊,后来参考了大神的思路http://blog.csdn.net/niushuai666/article/details/6981689,也看了好久,才把那个更新域搞懂。

这个题目,最重要的是处理关系域的更新,两个集合合并了之后,合并成一个集合,之间的关系就变了,就要更新他们之间的关系,其他的地方就和普通的并查集一样,这里才是并查集的本质吧。

#include <stdio.h>
#define MAXN 10000
struct node
{
    int father;
    int relation;//定义他们之间的关系
};
node s[MAXN];
int find(int x)//带动态压缩的查找
{
    int temp;
    if(x==s[x].father)
        return x;
    temp=s[x].father;
    s[x].father=find(temp);
    s[x].relation=(s[x].relation+s[temp].relation)%3;//关系域的更新,要找准他们之间的关系
    return s[x].father;
}
int main()
{
   int n,k,d,x,y,i,sum=0;
   int root1,root2;
   scanf("%d%d",&n,&k);
   for(i=0;i<n;i++)//初始化
   {
       s[i].father=i;
       s[i].relation=0;
   }
   for(i=0;i<k;i++)
   {
       scanf("%d%d%d",&d,&x,&y);
       if(x>n||y>n)
       {
           sum++;
           continue;
       }
       if(d==2&&x==y)
       {
             sum++;
             continue;
       }
       root1=find(x);
       root2=find(y);
       if(root1!=root2)//合并操作
       {
           s[root2].father=root1;
           s[root2].relation=(3+(d-1)+s[x].relation-s[y].relation)%3;//域的更新
       }
       else
       {
           if(d==1&&s[x].relation!=s[y].relation)
           {
             sum++;
             continue;
           }
           if(d==2&&((3-s[x].relation+s[y].relation)%3!=d-1))
           {
                sum++;
                continue;
           }
       }
   }
   printf("%d\n",sum);
    return 0;
}

这是一种做法,看到挑战程序设计竞赛里面的有另一种做法。这里也可以用不带秩的合并(内存可以少一点)。

#include<stdio.h>
#include<string.h>
 #define MAX 10000
 int parent[MAX],rank[MAX];//父亲,树的高度
int n,k,cnt;
 int find(int r)
 {
 int p=r;
 while(p!=parent[p]) p=parent[p];
 while(r!=p)
 {int temp=parent[r];parent[r]=p;r=temp; }
 return p;
 }
 void unionn(int r1,int r2)
 {
   r1=find(r1),r2=find(r2);
   if(rank[r1]<rank[r2])
   {parent[r1]=r2;      }
   else
   {
    parent[r2]=r1;
    if(rank[r1]==rank[r2]) rank[r1]++;//因为r2的父节点变成了r1,深度+1
   }
 }
 bool same(int a,int b)
 {
 return find(a)==find(b); //相等表示同一个集合
}
int main()
 {
 while(~scanf("%d%d",&n,&k))
 {
   cnt=0;
   for(int i=0;i<=3*n;i++)//这里赋值到3*n,一开始没注意到,然后结果也不对
  {
    parent[i]=i;
   }
   memset(rank,0,sizeof(rank));
   for(int i=0;i<k;i++)
   {
   int d,r1,r2;
   scanf("%d%d%d",&d,&r1,&r2);
   if(r1>n||r2>n)
   { cnt++;continue; }
  /*以下要做的,就是r1表示A种类,r1+n表示B,r1+2*n表示C ,r2同理。
   
   */
   if(d==1)//同类
  {
    if(same(r1,r2+n)||same(r1,r2+2*n) )
   /*这里是处理同类的2种情况,
     假如r1(A种类)和r2(B种类)是同一个集合,那么这肯定是假话
     假如r1(A种类)和r2(C种类)是同一个集合,也是假话
   */
    cnt++;
    else
    {unionn(r1,r2);unionn(r1+n,r2+n);unionn(r1+2*n,r2+2*n); }
   /*假如是真话,它们就是同类,分别合成一个集合,A与A,B与B,C与C */
   }
   else//x吃y
   {
    if(same(r1,r2)||same(r1,r2+2*n) )
     /*这里是处理x吃y的2种情况,
     假如r1(A种类)和r2(A种类)是同一个集合,不能同类相吃 ,是假话
     假如r1(A种类)和r2(C种类)是同一个集合,也是假话 ,只有C种类吃A种类,A种类只能吃B种类。
   */
    cnt++;
    else
    {unionn(r1,r2+n);unionn(r1+n,r2+2*n);unionn(r1+2*n,r2); }
   /*假如是真话,那么就让x吃y的关系合成一个集合,A种类吃B,B吃C,C吃A */
   }
   }
   printf("%d\n",cnt);
   
 }
  
 return 0;
 }


感觉还是太菜了,有些细节还是处理的不好,理解跟自己能够完全做出来还是两码事啊。。。  

以下是51单片机控制的12864液晶显示系统编程实现所需的代码: ``` #include <reg51.h> #include <intrins.h> #define LCD_DB P0 sbit LCD_RS = P2^6; // RS信号 sbit LCD_RW = P2^5; // RW信号 sbit LCD_E = P2^7; // E信号 sbit LCD_CS1 = P2^0; // CS1信号 sbit LCD_CS2 = P2^1; // CS2信号 void delay(unsigned int i) // 延时函数 { while(i--); } void write_com(unsigned char com) // 写命令函数 { LCD_RS = 0; LCD_RW = 0; LCD_DB = com; LCD_E = 1; _nop_(); _nop_(); LCD_E = 0; } void write_data(unsigned char dat) // 写数据函数 { LCD_RS = 1; LCD_RW = 0; LCD_DB = dat; LCD_E = 1; _nop_(); _nop_(); LCD_E = 0; } void init_lcd() // 初始化函数 { write_com(0xc0); // 设置显示模式 write_com(0x3f); // 允许显示 write_com(0x40); // 设置起始地址 write_com(0xb8); // 设置页地址 write_com(0xc0); // 设置列地址 write_com(0x40); // 设置起始地址 write_com(0xb8); // 设置页地址 write_com(0xc0); // 设置列地址 } void display_string(unsigned char x, unsigned char y, unsigned char *s) // 显示字符串函数 { unsigned char i; if(y == 0) // 第一行居中显示“南阳理工学院” { write_com(0xb8 + x); write_com(0x40); } else if(y == 1) // 第二行居中显示“www.nyist.edu.cn” { write_com(0xb8 + x); write_com(0x48); } else if(y == 2) // 第三行居中显示“电子信息工程专业” { write_com(0xb8 + x); write_com(0x50); } else if(y == 3) // 第四行居中显示尚春芳 { write_com(0xb8 + x); write_com(0x58); } for(i = 0; s[i] != '\0'; i++) { write_data(s[i]); } } void main() { init_lcd(); // 初始化LCD display_string(18, 0, "南阳理工学院"); // 显示第一行 display_string(16, 1, "www.nyist.edu.cn"); // 显示第二行 display_string(16, 2, "电子信息工程专业"); // 显示第三行 display_string(22, 3, "尚春芳"); // 显示第四行 while(1); } ``` 以上代码实现了你提出的要求,第一行居中显示“南阳理工学院”,第二行居中显示“www.nyist.edu.cn”,第三行居中显示“电子信息工程专业”,第四行居中显示尚春芳。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值