收藏 分享(赏)

并查集[讲义].ppt

上传人:weiwoduzun 文档编号:3270163 上传时间:2018-10-09 格式:PPT 页数:76 大小:234KB
下载 相关 举报
并查集[讲义].ppt_第1页
第1页 / 共76页
并查集[讲义].ppt_第2页
第2页 / 共76页
并查集[讲义].ppt_第3页
第3页 / 共76页
并查集[讲义].ppt_第4页
第4页 / 共76页
并查集[讲义].ppt_第5页
第5页 / 共76页
点击查看更多>>
资源描述

1、并查集初步及应用,引例:犯罪团伙 1、最小生成树 2、细胞个数 3、房间问题(noi94) 4、代码等式 5、银河英雄传说(noi2002),并查集的概念及运算,内容:,引例:【犯罪团伙】警察抓到了n个罪犯,警察根据经验知道他们属于不同的犯罪团伙,却不能判断有多少个团伙,但通过警察的审讯,知道其中的一些罪犯之间相互认识,已知同一犯罪团伙的成员之间直接或间接认识。有可能一个犯罪团伙只有一个人。请你根据已知罪犯之间的关系,确定犯罪团伙的数量。已知罪犯的编号从1至n。 输入: 第一行:n(=10000,罪犯数量), 第二行:m(=100000,关系数量) 以下若干行:每行两个数:I 和j,中间一个空

2、格隔开,表示罪犯i和罪犯j相互认识。 输出:一个整数,犯罪团伙的数量。,输入: 11 8 1 2 4 5 3 4 1 3 5 6 7 10 5 10 8 9,输出: 3,测试数据说明: 1 s 共10个测试数据: (1)5个数据:n=n=9000, 100000=m=90000;,建立无向图的模型。 如果x和y认识,结点x和y建立一条无向边。 求无向图的连通分量(dfs;bfs) 时间和空间!邻接矩阵:空间太大,超时。邻接表:空间满足,时间查过1s,最容易想到的算法:,抽象的算法:,开始把n个人看成n个独立集合。 每读入两个有联系的人i和j,查找i和j所在的集合p和q,如果p和q是同一个集合,

3、不作处理;如果p和q属于不同的集合,则合并p和q为一个集合。最后统计集合的个数即可得到问题的解。,需要将n个不同的元素划分成一组不相交的集合。开始时,每个元素自己成一个单元素集合,然后按照一定的顺序或问题给定的条件和要求将属于同一组元素(有特定关系)所在的集合合并,最后统计集合的个数往往就是问题的解。在这个过程中要反复的用到查询某个元素属于哪个集合的运算;两个不同集合合并的运算。适合描述这类问题的抽象数据结构类型称为并查集(合并与查找)。,一类问题模型:,并查集是一种树型的数据结构,用于处理一些不相交集合S=S1, S2, ,Sn, 每个集合Si都有一个特殊元素rootSi,称为集合的代表元.

4、,并查集支持三种操作: Init(X): 集合初始化:把元素xi加到集合Si中。每个集合Si只有一个独立的元素xi,并且元素xi就是集合Si的代表元素。Find(x): 查找:查找xi所在集合Si的代表rootSi。优化:路径压缩。 Union(x, y): 合并:把x和y所在的两个不同集合合并。,并查集的一个重要的应用是确定给定集合上的等价关系的个数。等价关系是一个具有自反、对称和传递三个性质的关系。等号“=”在实数集合R上是一个等价关系。对于实数中的任意x、y、z。一定满足下列关系:1)、x=x (自反性)2)、如果x=y,则y=x (对称性)3)、如果x=y,y=z,则x=z (传递性)

5、,【犯罪团伙】问题:,“同一团伙“抽象成无向图的”连通” ”连通”可以看成n个人的集合上的一个等价关系。 对于图中的任意3个顶点:A,B,C。有:1)、A连通A .(自反性)2)、如果A连通B,则B连通A .(对称性)3)、如果A连通B,B连通C,则A连通C .(传递性) 一个连通分量就是一个等价关系(连通),等价关系的个数就是连通分量的个数。 一个等价关系对应一个集合。 一个集团对应一个集合。,并查集的树型结构实现,采用树型结构实现并查集的基本思想是:每个子集合用一棵树来表示。树中的每个结点用于存放集合中的一个元素。树中的每个结点x设置一个指向父亲的指针。fatherx用根结点的元素代表该树

6、所表示的集合。,Init(X): 集合初始化:fatherxi=0(或者xi); 每个结点都是一颗独立的树, 是该树的代表元素。,三种操作:,Find(x): 查找: 查找x所在集合Si的代表rootSi。即:查找x所在树的树根结点(代表元素)。 顺着x往上找,直到找到根节点,也就确定了x所在的集合。,Union(x, y): 合并x和y所在的不同集合。p=find(x) ;q=find(y);if pq then fatherp=q 或 fatherq=p,3,11,6,输入: 11 8 1 2 4 5 3 4 1 3 5 6 7 10 5 10 8 9,11,初始化:,合并:,树根(集合代

7、表元素):Father1=0; father8=0; father11=0 孩子结点:father2=1; father4=3; father5=4; father9=8,查找:find(5)=1 find(7)=1 find(9)=8find(11)=11 find(1)=1 find(8)=8,合并:union(x,y)union(5,9)p=find(5)=1; q=find(9)=8;fatherq=p; 或 fatherp=q father8=1; father1=8,算法的 实现:, ai:为结点i的父亲指针。初始值为0,表示是树根。每个结点看成一颗树。 每读入两个结点x,y,找x

8、的树根p,令p=find(x);找y的树根q,令q=find(y);如果p=q,不做处理;如果属于不同的两棵树即:pq,则合并两棵树. 具体操作是: 把p看作q的孩子或者把q看作p的孩子:ap=q或者aq=p 最后统计树的数量,即ai=0的结点的数量,即问题的解:犯罪集团的个数。,【犯罪团伙】问题:,输入: 11 8 1 2 4 5 3 4 1 3 5 6 7 10 5 10 8 9,const max=10000; var a:array1maxof longint; /父亲指针i,j,m,n,ans,x,y,p,q:longint; function find(i:longint):lon

9、gint;非递归算法找i的根var j,k,t:longint;beginj:=i;/顺着结点i开始向上找,直到根为止。while aj0 do j:=aj; find:=j;end;begin,程序算法:,readln(n);/读入顶点数readln(m);/读入关系:边fillchar(a,sizeof(a),0);默认根标志是0,开始全是树根for i:=1 to m dobeginreadln(x,y);p:=find(x); 查找x的根q:=find(y); 查找y的根if pq then aq:=p; 合并p和q子树end;ans:=0;树根记数for i:=1 to n doif

10、 ai=0 then inc(ans);记录树根结点writeln(ans); end.,function find(i:longint):longint;/递归算法找i的根var j,k,t:longint;beginif ai=0 then exit(i); /若i为根,返回本身结点序号find:=find(ai); /否则继续向上找end;,Find(i)递归算法,改进运行时间的两种启发式策略:,1、按秩合并秩=树的高度高度小的树的根指高度向大树的根。减少整个树的高度,提高查找效率。 2、路径压缩根据等价关系的传递性,该变树的结构,使树变得扁平,从而提高查找效率。,Union(2,1)

11、Union(3,2) Union(3,4) Union(n,n-1) Find(1) Find(2) Find(n),按秩合并,N次查找总代价=n*(n+1)/2= (n2),O(nlgn),按秩合并,路径压缩:查找find(i)时进行,路径压缩实际上是在找完根结点之后,在回来的时候顺便把路径上元素的父亲指针都指向根结点这样可以减小以后的查找次数实现路径压缩的简单非递归算法:从结点到根走两遍:第一遍找根;第二遍是将路径上的所有结点的父亲都设为根。,Union(5,10),树越扁平查找效率越高。,路径压缩程序: 非递归算法: function find(i:longint):longint;非递

12、归算法找i的根var j,k,t:longint;beginj:=i;while aj0 do j:=aj; / 顺着i向上找根。find:=j;k:=i; 从i开始对子树结点进行路径压缩while ak0 do begin t:=k;k:=ak;at:=j;end;end;,function find(i:longint):longint; 采用递归算法:寻找结点i的树根,并对结点i所在的子树进行路径压缩,返回调整后的i的父指针(根)beginif ai=0 then exit(i);若i为根,返回本身结点序号find:=find(ai);递归找根结点ai:=find;路径压缩:找到根后,按

13、原路返回的同时,进行中间结点的逆序路径压缩,一次性完成end;,递归算法(多采用此法):,if aai=0 then exit(ai);若i的父结点为根,则返回父结点,constmax=10000; vara:array1maxof longint;i,j,m,n,sum,x,y,p,q:longint;function find(i:longint):longint;beginif ai=0 then exit(I);if aai=0 then exit(ai);find:=find(ai);ai:=find;end;procedure main;beginfillchar(a,sizeof

14、(a),0);readln(n); readln(m);for i:=1 to m dobeginreadln(x,y);p:=find(x); q:=find(y);if pq then aq:=p;end;,sum:=0;for i:=1 to n doif ai=0 then inc(sum end; procedure print;beginwriteln(sum);end;BEGINmain;print; END.,完整程序,并查集的时间复杂度,其中(n)是Ackermann函数的某个反函数,增长速度及其缓慢。 (n)=4。所以并查集的单次查找操作的时间复杂度也几乎是常数级的。,按秩

15、合并:时间:O(m*lg(n) 路径压缩:最坏:O(n+lg(n)) 按秩合并和路径压缩: O(m(n)经过启发式合并和路径压缩之后的并查集,执行m次查找的复杂度为O(m(n);算法导论p312 (犯罪集团:m=100000),n个元素的m次不相交集合操作,例1 最小生成树,用并查集实现kruskal算法,算法步骤: 1、把图中的边按权值从小到大排序。 2、按从小到大的顺序依次向树中加边。在添加每一条边(u,v)时,如果u和V两个点分别属于不同的两个集合(即两个点还没有连通,不在同一棵树上,否则加上就构成环),那么就加入这条边,否则处理下一条边。 3、直到添加n-1条边。,1,2,4,3,5,

16、17,30,10,24,7,23,5,1,2,3,4,5,Kruskal算法;sort; /排序边for i:=1 to n do fi:=0; /初始化根为0ans:=0; /价值for i:=1 to m do union(ei); /加边,procedure union(p:node); /检查边p是否能加到生成树中var x,y:integer;beginx:=find(p.u); /找u的根y:=find(p.v); /找v的根if xy then /不同根,构不成环,加入到树中begininc(ans, p.data);fy:=x; / 根合并end;end;,function f

17、ind(i:integer):integer; /查找i的父亲beginif fi=0 then exit(i); /i是根if ffi=0 then exit(fi); /i的父亲是根find:=find(fi); /递归查找fi:=find; /路径压缩end;,一矩形阵列由数字0到9组成,数字1到9代表细胞,细胞的定义为沿细胞数字上下左右还是细胞数字则为同一细胞,求给定矩形阵列的细胞个数。如阵列: 0234500067 1034560500 2045600671 0000000089 有4个细胞。 输入:整数m,n(m行,n列=3000) M*n的矩阵,数据之间无空格; 输出:细胞的个数

18、。,例2、细胞统计,0234500067 1034560500 2045600671 0000000089,算法分析:,搜索可以实现(dfs,bfs),dx:array14of integer=(1,0,-1,0); dy:array14of integer=(0,1,0,-1);,/逐行逐列扫描: ans:=0;for i:=1 to n dofor j:=1 to m do if bi,j thenbegin inc(ans); try(i,j); end;,procedure try(i,j:integer);/dfsvar k:integer;beginbi,j:=false;/访问标

19、记for k:=1 to 4 doif bi+dxk,j+dyk then try(i+dxk,j+dyk);end;,并查集实现, 逐行扫描,依次处理每一个点 初始化:每个点的父亲指向本身/每个数是独立的一个细胞 处理点A(I,j): 3种情况如下: 如果B和C都是细胞:合并P=find(c); q=find(b);If pq then fatherp=qfatherA=q; 如果B是而C不是,则 fatherA=B 或fatherA=find(B) 如果B不是而C是,则 fatherA=C 或fatherA=find(C) 统计父亲是自身(find(i)=i)的结点数即细胞的个数,cons

20、t maxn=3000; type point=record x,y:integer; /行与列end;a:array1maxn,1maxnof point; /父亲结点b:array0maxn,0maxnof boolean; /是否是细胞,算法的实现:,/数据读入readln(n,m);for i:=1 to n dobeginfor j:=1 to m dobeginread(ch);bi,j:=ch0;if bi,j thenbegin ai,j.x:=i; ai,j.y:=j; end; / 初始化父亲指向自身end;readln;end;,for i:=1 to n dofor j

21、:=1 to m doif bi,j thenbeginif bi-1,j and bi,j-1 then /左与上是细胞beginp:=find(i-1,j); q:=find(i,j-1);if (p.xq.x)or(p.yq.y) then ap.x,p.y:=q;ai,j:=q;end;if bi-1,jand not bi,j-1 then /上是左不是begin ai,j.x:=i-1;ai,j.y:=j; end;if bi,j-1 and not bi-1,j then /上不是左是begin ai,j.x:=i;ai,j.y:=j-1; end;end;,逐行扫描处理每个细胞

22、结点,function find(x0,y0:longint):point; /查找当前位置(x0,y0)细胞的父亲结点beginif (x0=ax0,y0.x) and (y0=ax0,y0.y) then exit(ax0,y0);find:=find(ax0,y0.x,ax0,y0.y);ax0,y0:=find; /路径压缩end;,/统计细胞的个数:父亲指向自己的结点数量tot:=0;for i:=1 to n dofor j:=1 to m doif (ai,j.x=i) and (ai,j.y=j) then inc(tot);,例3、房间问题(NOI94),下图是一张建筑平面图

23、,编程计算 1、该建筑中有多少个房间; 2、最大的房间有多大; 3、拆除建筑中的某一堵墙,以形成一个尽可能大的房间。指出该墙。 该建筑分成个方块(m50,n50),每个方块可有04堵墙(0表示无墙)。,输入数据: 用一个数字表示一个方块。 文件开始是北南方向的方块数,其次是东西方向的方块数。 后面的行中,每个方块中墙的特征由数字P来描述(0P15)。数字P是下面的可能取的数字之和: 1(西墙 west) 2(北墙 north) 4(东墙 east) 8(南墙 south) 室内的墙被定义两次: 例如方块 (1, 1)中的南墙也被位于其南面的方块(2,1 )定义了一次。 建筑中至少有两个房间。

24、在我们的例子中文件INPUT.TXT的内容为: 输出数据: 第1行写房间总数; 第2行写最大房间的面积(方块数); 第3行写你的建议,指明应拆除位于哪两个方格间的一堵墙 第4行拆除后形成的最大房间面积。在我们的例子中,第3行是 (3,3)(3,4),这是各种解中的一个,你只要给出其中一个即可。,样例输入: 4 7 11 6 11 6 3 10 6 7 9 6 13 5 15 5 1 10 12 7 13 7 5 13 11 10 8 10 12 13,问题:1、该建筑中有多少个房间; 2、最大的房间有多大; 3、拆除建筑中的某一堵墙,以形成一个尽可能大的房间。指出该墙。4、拆除后形成的最大房间

25、面积,输出: 5 9 (3,3)(3,3) 16,7,9,3,8,1,算法分析:,类似细胞统计,第一问:该建筑中有多少个房间;逐行扫描,处理当前格子与上方和左方的格子。最后统计,第二问:最大的房间有多大;父亲格子设置一个统计格子数量的变量,每扫描到一个以当前格子为父亲的格子,数量加1。,第三问:拆除建筑中的某一堵墙,以形成一个尽可能大的房间。指出该墙。枚举每一堵墙如果是竖直方向的墙:看左右是否连通,如果连通(同父亲),拆除后不改变面积;不连通则求左右两部分面积和,比较。如果水平的墙:看上下两部分。,读入数据的处理技巧:, 怎样判断某个格子的四周墙的情况,哪个方向有墙?,格子的值是x;考察二进制

26、数:1111 W:x mod 2=1; N:x div 2 mod 2=1; E:x div 4 mod 2=1; S:x div 8 mod 2=1;,样例输入: 4 7 11 6 11 6 3 10 6 7 9 6 13 5 15 5 1 10 12 7 13 7 5 13 11 10 8 10 12 13,方法一:用一个二维数组记下每个格子的数值aI,j,在使用时直接根据数值特征判断四周墙的情况。,方法二:用布尔数组记下北墙和西墙的情况。,for i:=1 to n dofor j:=1 to m dobeginread(x);westi,j:=x mod 2=1;northi,j:=

27、x div 2 mod 2=1;end;,readln(n);readln(m);for i:=1 to n dofor j:=1 to m dobeginread(x);westi,j:=x mod 2=1;northi,j:= x div 2 mod 2=1;end;,typenode=recordx,y:integer;end; varwest,north:array1max,1max of boolean;/记录北西墙square:array1max,1max of longint; /房间的面积root:array1max,1max of node; /每个格子的父亲n,m:inte

28、ger;total,maxsquare,best:longint; /房间数量,最大房间面积,拆除某一墙后形成的房间面积x,y:integer; /记录当前的格子d:string;/记录拆除墙的方向,/读入格子的情况 procedure init;var i,j,x:integer;beginfillchar(west,sizeof(west),false);fillchar(north,sizeof(north),false);readln(n);readln(m);for i:=1 to n dofor j:=1 to m dobeginread(x);westi,j:=x mod 2=1

29、; /有西墙northi,j:= x div 2 mod 2=1;/有北墙end;end;,/查找格子(i,j)的父亲格子 function find(i,j:integer):node;beginif (rooti,j.x=i) and(rooti,j.y=j) then exit(rooti,j);if (rootrooti,j.x,rooti,j.y.x=rooti,j.x)and(rootrooti,j.x,rooti,j.y.y=rooti,j.y)then exit(rooti,j);find:=find(rooti,j.x,rooti,j.y);rooti,j:=find;end

30、;,procedure makeroot; /处理每个格子,集合的合并过程var i,j:integer;p,q:node;beginfor i:=1 to n dofor j:=1 to m dobegin rooti,j.x:=i;rooti,j.y:=j;end; /格子父亲初始化。for i:=1 to n dofor j:=1 to m dobeginif (not westi,j)and(not northi,j) then /左上没墙beginp:=find(i,j-1);q:=find(i-1,j);if (p.xq.x)or(p.yq.y) then rootp.x,p.y:

31、=q;rooti,j:=q;end;if (not westi,j)and(northi,j) then rooti,j:=find(i,j-1);/左没,上有if (westi,j)and(not northi,j) then rooti,j:=find(i-1,j);/左右,上没有end;for i:=1 to n dofor j:=1 to m do rooti,j:=find(i,j); /全部扫描,procedure work;/计算过程var i,j:integer;beginfillchar(square,sizeof(square),0);total:=0; /房间数量maxs

32、quare:=0;/最大房间面积for i:=1 to n dofor j:=1 to m dobeginif (rooti,j.x=i) and (rooti,j.y=j) then inc(total);/统计根的数量inc(squarerooti,j.x,rooti,j.y);/父亲面积加1end;for i:=1 to n do /找最大房间面积for j:=1 to m doif maxsquaresquarei,j then maxsquare:=squarei,j;,/判断拆除哪一个墙使房间面积变得最大best:=0;for i:=1 to n do for j:=1 to m

33、dobegin/ 拆当前格子的西墙if (j1)and(rooti,j-1.xrooti,j.x)or(rooti,j-1.yrooti,j.y)and(best1)and(rooti-1,j.xrooti,j.x)or(rooti-1,j.yrooti,j.y)and(bestsquarerooti-1,j.x,rooti-1,j.y+squarerooti,j.x,rooti,j.y) thenbeginbest:=squarerooti-1,j.x,rooti-1,j.y +squarerooti,j.x,rooti,j.y;x:=i; y:=j; d:=N; /记下当前格子及北墙:上e

34、nd;end;,procedure print;/输出结果beginassign(output,fout);rewrite(output);writeln(total);writeln(maxsquare);if d=W then writeln(,x, ,y-1,),(,x, ,y,);if d=N then writeln(,x-1, ,y,),(,x, ,y,);writeln(best);close(output);end;,/主程序 BEGININIT;makeroot;WORK;print; END.,例4、二进制方程,【问题描述:】一个形如:X1X2Xn=Y1Y2Ym 的等式称为

35、二进制方程。在二进制方程的两边:Xi和Yj (1=i=n;1=j=m)是二进制数字(0、1)或者一个变量(小写字母)。每个变量都是一个有固定长度的二进制代码,他可以在等式中取代变量的位置,称这个长度为变量的长度。为了解一个二进制方程,需要给其中的变量赋予适当的二进制代码,使得我们用他们替代等式中的相应的变量后(等式的两边都变成二进制代码),这个等式成立。 编程任务: 对于每一个给出的方程,计算一共有多少组解。已知变量最多有26个(26个英文小写字母),且等式的每一端的数字和变量的长度之和不超过10000。 【输入:】 第一行:k(k=26,变量的个数,规定使用小写英文字母中的前k个字母作为变量

36、,如k=5,则变量a,b,c,d,e)。 第二行:k个正整数,中间用一个空格隔开,依次代表k个变量的长度。 第三行:等式左边的表达式。 第四行:等式右边的表达式。 【输出:】 等式中出现的变量共有多少组解。,样例1输入: 2 4 2 1b1 a 样例1输出: 4 (说明: 两个变量:a,b 长度分别为:4 和2 4组解 1 、a=1001; b=00 2、 a=1011; b=01 3、 a=1101; b=10 4、 a=1111; b=11),样例2输入: 5 4 2 4 4 2 1bad1 acbe 样例2输出: 16(样例说明: K=5,变量:a,b,c,d,e。长度分别为:4 2 4

37、 4 2。等式是:1bad1= acbe 输出16,即变量a,b,c,d,e共有16组解。),样例1输入: 2 4 2 1b1 a 样例1输出: 4 (说明: 两个变量:a,b 长度分别为:4 和2 4组解 1 、a=1001; b=00 2、 a=1011; b=01 3、 a=1101; b=10 4、 a=1111; b=11),分析:,等式:1b1=a可以写成等价的等式: 1 b1 b2 1=a1 a2 a3 a4可以得到如下的等式:a1=1;a2=b1;a3=b2;a4=1。a1,a4唯一确定,a2=b1,a3=b2有两个等价类,均可以取0和1两种情况。故有4组解。,样例2: 方程:

38、1bad1= acbe 写成等价式:1 b1 b2 a1 a2 a3 a4 d1 d2 d3 d4 1=a1 a2 a3 a4 c1 c2 c3 c4 b1 b2 e1 e2 得出:1=a1 b1=a2 b2=a3 a1=a4 a2=c1 a3=c2 a4=c3 d1=c4 d2=b1 d3=b2 d4=e1 1=e2 整理以上等式得出:1=a1=a4=c3=e2 (确定)b1=a2=c5=d2b2=a3=c2=d3d1=c4d4=e1 得到4个未知的等价类:每个等价类都有0和1两种情况: 故共有24=16组解。,样例2输入: 5 4 2 4 4 2 1bad1 acbe 样例2输出: 16,

39、结论:,把每个长度为k的变量为k个长度为1的变量,则结果不变。 这样我们可以得到n个等式(n为等号两边的长度)。 每个等式可以确定某两个变量相等,因此我们可以得到一系列的等价关系。 用并查集很容易在近似O(n)的时间复杂度下找到所有的等价类。只要不包含0和1的等价类取值均有两种情况。 因此如果等价类个个数为p,则2p就是解的个数。,p=n(10000),高精度运算,将等式的两边展开后,为了表示等价关系,我们可以将每个变量对应一个数值,以便于找等价关系:,0:0 1:1 a1:2 a2:3 a3:4 a4:5 b1:6 b2:7 ,样例2输入: 5 4 2 4 4 2 1bad1 acbe 样例

40、2输出: 16,判断等式,把等式中出现的变量与数值一一对应,根据等价关系:把变量相同等价成相应数值在同一集合的问题。 如:a2=b1对应:3和6在同一集合。 把变量的等价关系化为数值的等价关系,便于处理。,样例2输入: 5 4 2 4 4 2 1bad1 acbe 样例2输出: 16,0:0 1:1 a1:2 a2:3 a3:4 a4:5 b1:6 b2:7 c1: 8 c2: 9 c3: 10 c4: 11 d1: 12 d2: 13 d3: 14 d4: 15 e1: 16 e2: 17,样例2: 方程:1bad1= acbe 写成等价式:1 b1 b2 a1 a2 a3 a4 d1 d2

41、 d3 d4 1=a1 a2 a3 a4 c1 c2 c3 c4 b1 b2 e1 e2 得出:1=a1 b1=a2 b2=a3 a1=a4 a2=c1 a3=c2 a4=c3 d1=c4 d2=b1 d3=b2 d4=e1 1=e2 整理以上等式得出:1=a1=a4=c3=e2 (确定)b1=a2=c1=d2b2=a3=c2=d3d1=c4d4=e1 对应数值的关系: 1=2=5=10=17 6=3=8=13 7=4=9=14 12=11 15=16,4个等价类,无解的情况: 1)等式两边转换后长度不相等。 2)0和1在同一集合内。 注意:集合内有0或1的解是唯一的,不能乘2,等式中没出现的

42、变量不应计算。,有5个变量:a,b,c,d,e。长度分别为:4 2 4 4 2。 等式:1b0=a 可以写成等价的等式:1 b1 b2 0=a1 a2 a3 a4 可以得到如下的等式:a1=1;a2=b1;a3=b2;a4=0。给定的变量有abcde,但等式中仅涉及ab,所以有4组解 。 如果考虑cde则有212=4096组解。,typeab=array010001of longint;data=array03020 of integer;/高精度结果varsa,sb:ab;变量的对应值b:array0max of boolean;标记该变量是否在等式中出现len,sum,l:longint;

43、tot:data;,最大210000:10000*log(2)=3010,procedure init;varta,tb,n,i:longint;ch:char;a:arrayazofrecord start,long:longint; end;start:变量在sa,sb中出现的位置,long:变量的长度procedure make(var sa:ab; var ta:longint);把等式两边的表达式转化成对应的数值var i:longint; ch:char;beginfillchar(b,sizeof(b),false);ta:=0;while not eoln dobeginrea

44、d(ch);if (ch=0) or (ch=1) thenbegin inc(ta); sata:=ord(ch)-48; endelsefor i:=1 to ach.long dobegin inc(ta); sata:=ach.start+i; bsata:=true;end;end;end;,BEGIN initassign(input,fin); reset(input);readln(n);sum:=1;for i:=1 to n do 定义变量的对应值beginch:=chr(96+i);ach.start:=sum;read(ach.long);sum:=sum+ach.lo

45、ng;end;readln;make(sa,ta); readln; make(sb,tb);if tatb then tot1:=-1;长度不等时,无解len:=ta;close(input);END;init,BEGIN workif tot1=-1 then begin print(0);exit;end;for i:=0 to sum do ai:=-1;集合初始化for i:=1 to len do /len:等式的长度beginp:=find(sai);q:=find(sbi);if pq then ap:=q;对应的变量如果不在一个集合则合并所在的集合end;check; END

46、.,procedure check;var i:longint;beginif find(0)=find(1) then 0和1在一个集合中,无解begin print(0);exit; end;tot1:=1;l:=1;for i:=2 to sum do /统计等价类:集合的个数,从而求出解的组数:是根结点但又不包含0或者1的集合个数if (find(i)=i) and bi and (ifind(0)and (ifind(1) then mul2;print(1);end;,function find(i:longint):longint;查找,路径压缩,合并集合beginif ai=-

47、1 then exit(i);find:=find(ai);ai:=find;end;,【问题描述】公元五八一年,地球居民迁移至金牛座第二行星,在那里发表银河联邦创立宣言,同年改元为宇宙历元年,并开始向银河系深处拓展。宇宙历七九九年,银河系的两大军事集团在巴米利恩星域爆发战争。泰山压顶集团派宇宙舰队司令莱因哈特率领十万余艘战舰出征,气吞山河集团点名将杨威利组织麾下三万艘战舰迎敌。杨威利擅长排兵布阵,巧妙运用各种战术屡次以少胜多,难免恣生骄气。在这次决战中,他将巴米利恩星域战场划分成30000列,每列依次编号为1, 2, , 30000。之后,他把自己的战舰也依次编号为1, 2, , 30000,让第i号战舰处于第i列(i = 1, 2, , 30000),形成“一字长蛇阵”,诱敌深入。这是初始阵形。当进犯之敌到达时,杨威利会多次发布合并指令,将大部分战舰集中在某几列上,实施密集攻击。合并指令为M i j,含义为让第i号战舰所在的整个战舰队列,作为一个整体(头在前尾在后)接至第j号战舰所在的战舰队列的尾部。显然战舰队列是由处于同一列的一个或多个战舰组成的。合并指令的执行结果会使队列增大。,

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报