zoukankan      html  css  js  c++  java
  • 关系根节点[置顶] NYOJ207 VS POJ1182 食物链

    这几周个人几篇文章介绍了改关系根节点的文章. 关联文章的地址

        标题链接:

        http://acm.nyist.net/JudgeOnline/problem.php?pid=207

                        

        http://poj.org/problem?id=1182

        标题析分:

        首先这道题是用

        

        带权的并查集

        

        来做,其实这个我自己经已想到了。不过思绪很不晰清。然后看了别人的一些理处巧技,很不错,在这里析分一下。当然我会力尽的把析分写得细详一些,因为可能往后我自己也要需回首来看看的,好了,话废多不说了。

        在说之前,首先要确明这道题的

        

        大体想思

        

        。

        1、如果两个物种有联系,是管不吃,被吃还是同类,它们之间应该是有一条径路可达的,也就是它们在一个合集中。

        2、如果a,b有关系,b,c有关系,那么a,c之间的关系式可以通过两者的关系推出来的。

        好,面下绕围着上面的两个想思来逐一析分。

        首先就是

        

        怎么把有关系的物种放到同一个合集中去

        

        。这就要需用到并查集了。每一次入输d,x,y,也就是相当于x,y之间有一条权为d的径路。先略忽这个权值,直接斟酌径路,那并查集的径路立建就用不我说了。一个parent数组,parent[i]表现从parent[i]到i有一条径路。OK,那不同的物食圈就构成了一个连通区域。每个连通区域都有一个根点节。

        面下斟酌怎么理处这个权

        。先说点数学的货色,任何一种偏序关系都足满自反、对称、传递。

        

        自反

        

        :自己跟自己足满偏序关系。

        

        对称

        

        :a,b的偏序关系为r,则b,a的偏序关系为~r.表现求反。

        

        传递

        

        :a,b的偏序关系为r1,b,c的偏序关系为r2,a,c的偏序关系为r1+r2.这里的+表现一种直和吧,符号打不出来,一个圆圈里头有一个加号。

        为了便利,用一个relation数组来维护这个权值。relation[i]表现的是i在所的连通区域的根点节到i的关系。先略忽这个关系数组的维护过程,把团体的思绪理清晰。如果有两个物种加进来,就有两种情况,要么它们在同一个连通集里头。要么不在同一个连通集里头。

        

        两者在同一个连通集里头

        

        1、新加的关系明表x,y是同类,那么它们两个分别到连通区域根点节的关系应该是一样的,要不就盾矛了。(记为case1)

        2、如果新加的关系明表x,y不是同类,那么在参加y当前,x相对根点节的关系和x本来相对根点节的关系应该是变不的,否则就盾矛了。(记为case2)

        两者在不同的连通集里头

        ,就直接连接两个连通集就能够了。记为case3)

        径路收缩理处

        由于后来物种会越来越多,我们不望希物食链拉的很长,所以会尽可能的让全部的点节都直接和根点节相连。这样个整连通的图就有点呈现出星形。

        怎么维护关系数组。

        数组里头的每个元素的取值要么是0(同类),要么是1(父吃子),要么是2(子吃父)。至于为什么要这么设置,参考一另篇博客

        http://blog.csdn.net/c0de4fun/article/details/7318642

        ,这里我不想话废,我只想理清怎么搞这个关系递推。设假面前的数据我经已理处好了,在现要理处d,x,y.为了叙说的便利,记relation[x]为x根->x.那么在现就有三种情况:

        

        case1:

        

        种这情况x根与y根雷同。如果x根->x与y根->y不同,明表x,y不是同类,与d=1盾矛。

        

        case2:

        

        种这情况x根与y根雷同。如果参加y之后,x根->x=x根(即y根)->y+y->x,如果新求出来的关系与本身已有的x根->x的关系不同,则盾矛。

        

        case3:

        

        每日一道理
    冰心说道:“爱在左,同情在右,走在生命的两旁,随时撒种,随时开花,将这一径长途,点缀得香花弥漫,使穿枝拂叶的行人,踏着荆棘,不觉得痛苦,有泪可落,却不是悲凉。”

        种这情况x根与y根不同。由于这里添加的是x到y的一条有向边。将y根的父点节设置为x根,更新y根父点节到x根的关系,即x根->y根=x根->x+x->y+y->y根,由于这里都是有向边,所以更新关系的时候意注关系的方向。这里要需意注,我们只更新了两个根之间的关系,x根与来原的y在所的连通区域里头的点节的关系都没有更新,这就是为什么要在一开始判断之前就要调用Find函数,更新每个点节到其根点节的关系。

        

        初始条件:

        

        有了这个递推,就好办了。初始条件parent就是并查集一般的初始条件,父点节于等自己。由于初始的时候父点节时自己,当然自己跟自己的关系肯定是同类咯,也就是relation[i]=0

        当然思绪还有点不清晰也系关没,码代中也给出了响应的释注。望希能更好的解理题问。

    #include<stdio.h>
    const int N = 50005;
    int parent[N];
    int relation[N];//根点节到点节的关系
    void Init(int n)
    {
    	for(int i = 0; i <= n; ++i)
    	{
    		parent[i]= i;
    		relation[i] = 0;
    	}
    }
    //更新的步调,先将当前点节与其根点节相连,然后更新其与根点节的关系
    //当前点节x与根点节r的关系更新的方法:(x与其父点节的关系+其父点节的关系与根点节的关系)%3
    //所以在更新点节x的数据之前要需更新其父点节的数据,这是Find为什么搞成递归函数的原因
    //其更新的次序是从根点节始终往下,始终到当前点节x的父点节。
    int Find(int x)
    {
    	if(x != parent[x])//不是根点节
    	{
    		int temp = parent[x];
    		//将当前点节的父点节设置为根点节
    		parent[x] = Find(temp);
    		//更新当前点节与根点节的关系,由x->x父和x父->父根的关系失掉x->父根的关系
    		//所以在这之前必须更新其父点节与根点节的关系
    		relation[x] = (relation[x] + relation[temp]) % 3;
    	}
    	return parent[x];
    }
    
    int main()
    {
    	int n,m,i;
    	int x,y,d;
    	int rx,ry;
    	int cnt;
    	while(scanf("%d %d", &n, &m) != EOF)//POJ上只要需一次入输,所以不要需while循环
    	{
    		cnt = 0;
    		Init(n);
    		for(i = 0; i < m; ++i)
    		{
    			scanf("%d %d %d", &d, &x, &y);
    			if(x > n || y > n)
    			{
    				++cnt;
    				continue;
    			}
    			if(d == 2 && x == y)
    			{
    				++cnt;
    				continue;
    			}
    			rx = Find(x);
    			ry = Find(y);
    			if(rx == ry)//属于同一个子集
    			{
    				//如果x、y是同类,那么他们相对根点节的关系应该是一样的
    				//如果不是同类,参加y之后,x相对根点节的关系(x根->y,y->x(即3-(d-1)=2).即x根->x)应该是变不的
    				if((d == 1 && relation[x] != relation[y]) ||
    					(d == 2 && relation[x] != (relation[y] + 2)%3))
    					++cnt;
    			}
    			else//合并两个连通区域
    			{
    				parent[ry] = rx;//y根的父点节更新成x根
    				//(d - 1)为x与y的关系,3-relation[y]是y与y的根点节的关系,意注方向,relation[x]是其根点节与x的关系
    				//x根->x,x->y,y->y根:即x根->y根
    				relation[ry] = (relation[x] + d - 1 + 3 - relation[y]) % 3;
    			}
    		}
    		printf("%d\n", cnt);
    	}
    	return 0;
    }

    文章结束给大家分享下程序员的一些笑话语录: 马云喜欢把自己包装成教主,张朝阳喜欢把自己包装成明星,李彦宏喜欢把自己包装成的很知性,丁磊喜欢把自己包装的有创意,李开复总摆出一副叫兽的样子。看来的。其实我想说,缺啥补啥,人之常情。

  • 相关阅读:
    NPM (node package manager) 入门
    win10 环境 gitbash 显示中文乱码问题处理
    javascript中的Array对象 —— 数组的合并、转换、迭代、排序、堆栈
    Javascript 的执行环境(execution context)和作用域(scope)及垃圾回收
    Centos 下 mysql root 密码重置
    执行 $Gulp 时发生了什么 —— 基于 Gulp 的前端集成解决方案(二)
    Java I/O输入输出流详解
    反射---Java高级开发必须懂的
    细说Java多线程之内存可见性
    全面解析java注解
  • 原文地址:https://www.cnblogs.com/xinyuyuanm/p/3049969.html
Copyright © 2011-2022 走看看