1、解题报告5整数因子团问题整数因子团问题解题报告1、题目描述整数因子团是一个有趣的智力游戏。游戏规则如下: 1 给定由n个连续自然数组成的序列1,n。2 当前序列中选择一整数,它在当前序列中至少有2个因子(该整数本身可以算作一个因子,例如,整数60的所有因子是:60,30,20,15,12,10,6,5,4,3,2,1。它们构成整数60在当前序列中的因子团 )。 3 从当前序列中删去所选整数的所有因子。 4 重复步骤2和3,直到空序列或当前序列中所有数字在序列中仅有一个因子。你在游戏中的得分是步骤2中所选择的数字的总和。例如,当n=6时,给定的序列为1,2,3,4,5,6。一个游戏过程是:步骤:
2、选择整数5;步骤:从当前序列中删去整数5的所有因子5,1。当前序列改变为:2,3,4,6。步骤:选择整数6;步骤:从当前序列中删去整数6的所有因子6,2,3。当前序列改变为:4。游戏结束。该游戏的得分是11。另一个游戏过程如下:步骤:选择整数5;步骤:从当前序列中删去整数5的所有因子5,1。当前序列改变为:2,3,4,6。步骤:选择整数4;步骤:从当前序列中删去整数4的所有因子4,2。当前序列改变为:3,6。步骤:选择整数6;步骤:从当前序列中删去整数6的所有因子6,3。当前序列改变为空序列。游戏结束。该游戏的得分是15。从上面的例子不难看出,游戏的步骤2所选数字的次序对游戏的得分有很大影响。
3、应该如何选择才能使游戏获得最大得分?2n120。如n=6时的解为15;n=15时的解为81;n=30时的解为301;n=50时的解为808;n=65的解为1328。2、算法分析。看看n=6时的例子,我们很容易得到答案。再大一点呢?例如n=15时,还能很快得到81的解吗?也不是很难,因为人在玩游戏的过程中容易得出一个比较优的决策。在数据比较小时,还能够比较快的得到最优决策。这是因为我们在用一个策略贪心策略!的确,由于题目的简明清晰,很容易想出各种贪心方法。这些贪心方法,有的能够保证最优,有的却不能保证。但是,这是一道求最优解的题。贪心的思想只能作为一个参考,并不能解决此题。弄了很久,很难找到比较
4、好的方法,最后,搜索似乎是唯一的出路。搜就搜吧。看到数据范围很容易受惊,太大了!要能够高效率,就必须有很强的剪枝。在一般情况下,如果求出了一个下界,用最优性剪枝可能会比较有效果。这个下界,可以首先贪心得到,也可以在搜索过程中贪心的规定搜索顺序。但是,这个是最简单的剪枝,它还远远不够。最优性剪枝的判断方法是,如果当前得到的分数加上后面最多能够得到的分数还不超过现在的最优解,那么就可以剪枝。“后面最多能够得到的分数”这意味着我们只求出下界是不够的,还需要求出上界。这个上界看起来不是很好求,就用剩下的所有数的和代替?太不像样了。要得到一个像样的上界,就必须分析这堆数的规律。首先,把这些数和它们的关系
5、用图的方法表现出来。用n个节点代表n个数,如果a是b的倍数,那么就从a到b连一条有向边。显然,这个倍数关系是一个偏序关系,由于它的传递性,可以去掉一些边,使得从剩下的边就可以看出一个偏序关系。如果a是b的倍数,存在bcb-c-d,那么先删除c再删除a与直接删除a得到的状态是一样的,但是前者得到的分数要多。情况c):有一个层次为level(a)-1的点b,除了a,b外的点的层次都是level(a)-2。这种情况是不会出现的,由于至少有两个层次为level(a)-2的点,取两个c,d。那么b到c,b到d都有边。设p1=b/c,p2=b/d,显然p1p2并且p1,p2都是质数,那么考虑a/p1他一定
6、是c的倍数。由结论知a/p1一定没有被删除。同样a/p2也没有被删除,但是却只有一个层次为level(a)-1的点,这显然不可能。即情况c)不会出现。不难发现上面已经包含了所有的情况。这个可以用来剪枝。另外,在有些情况下能够保证最优,例如:猜想:一个数只有两个约数没有被删除,并且这个数的没有倍数(除了自己),并且它是满足前面条件的最大的数,那么删除这个数是最优的。我不能证明猜想,但是可以证明的是,在n120时不会有反例,而在n较大时似乎有一个这样的例子,这个只是在证明时构造出来的一个反例,实际中也许不会出现,下图表示。然后,有些删除操作可以交换,即顺序不同但得到的分数和剩下的状态是一样的。为了
7、避免重复,可以考虑相邻两个操作能不能交换的情况。还有,在求最大匹配的时候,不需要每次都重新找增广轨,只需要在以前找到匹配的基础上修改,这个修改,不需要是增广轨。它是一条匹配便于没有匹配边的交替轨,它可以是偶数条边,只需要改变的权值大于0就可以了。至此,算法已经基本成形了。如果用上上面所有的优化,包括没有证明的猜想,实践证明效果非常好。下面是一些测试的结果:N1030506580100105110120答案40301808132820413164343437644593时间(单位:s)0.000.000.000.000.000.050.360.220.22测试环境:Pentium 4 1.70G
8、Hz CPU Free pascal v 1.0.6。所有的数据加起来都只用了2.14s。奇怪的是,如果不用猜想,那么在n从70到74,81到86,和从105到120这一段速度奇慢,其余的都是很快,下面的数据可以说明:n707172737481828384,85,105120答案15661570164216441718210521872191/时间(不用猜想)110.99116.2951.9266.76154.8942.4289.4595.22Too long 除了没有运行出来的点不知道外,其余的点都没有找到猜想的反例。参考文献 算法艺术与计算机竞赛 刘汝佳 黄亮讨论 向刘汝佳请教过,知道题目
9、还没有找到多项式算法,然后从他书上的提示找算法。感谢 刘汝佳3、程序const maxn=120; maxnode=500; size=127; inputfile=taxman.in; outputfile=taxman.out;type tsearchsystem=record f,g:longint; match,number:array1.maxnof integer; cut:array1.maxnof boolean; end;var getso:boolean; n,tans:integer; lower:longint; point,level:array1.maxn+1of
10、integer; node,value:array1.maxnodeof integer; searchsys:array0.maxn shr 1of tsearchsystem; ans,nowans:array0.maxn shr 1of integer;procedure prepare;var i,j,p:integer; prime:array1.maxnof boolean;begin fillchar(prime,sizeof(prime),1); for i:=2 to n do if primei then for j:=i to n div i do primei*j:=f
11、alse; level1:=0; for i:=2 to n do for j:=2 to i do if i mod j=0 then begin leveli:=leveli div j+1; break end; p:=0; for i:=1 to n do begin pointi:=p+1; for j:=1 to i-1 do if (i mod j=0) and primei div j then begin inc(p);nodep:=j;valuep:=i end; for j:=2 to n div i do if primej then begin inc(p);node
12、p:=i*j;valuep:=i*j end end; pointn+1:=p+1;end;function find(var sys:tsearchsystem;direct:boolean):boolean;var i,k,head,tail,t:integer; q:array0.sizeof integer; inq:array1.maxnof boolean; dist,par:array1.maxnof integer;begin with sys do begin head:=0;tail:=0; fillchar(par,sizeof(par),0); fillchar(inq
13、,sizeof(inq),0); for i:=1 to n do if (matchi=0)and(odd(leveli)=direct)and not cuti then begin inc(tail);qtail:=i;disti:=0;inqi:=true; end else disti:=-maxint; while headtail do begin inc(head); k:=qhead and size; inqk:=false; if odd(levelk)=direct then begin for i:=pointk to pointk+1-1 do if (nodeim
14、atchk)and not cutnodei then begin t:=nodei; if cutt then continue; if distk+valueidistt then begin distt:=distk+valuei; part:=k; if not inqt then begin inc(tail); qtail and size:=t; inqt:=true end end end end else begin if matchk0 then begin if matchkk then t:=matchk else t:=k; if distk-tdistmatchk
15、then begin distmatchk:=distk-t; parmatchk:=k; if not inqmatchk then begin inc(tail); qtail and size:=matchk; inqmatchk:=true end end end end end; t:=-maxint; for i:=1 to n do if (odd(leveli)xor direct)and(distit)and(matchi=0)and not cuti then begin k:=i;t:=disti end; if t0 then begin find:=true; inc
16、(g,t); while k0 do begin i:=park; matchi:=k;matchk:=i; k:=pari end; exit end; t:=-maxint; for i:=1 to n do if (odd(leveli)=direct)and(distit)and not cuti then begin k:=i;t:=disti end; if t0 then begin find:=true; inc(g,t); i:=matchk; matchk:=0; while i0 do begin k:=pari; matchk:=i;matchi:=k; i:=park
17、 end end; find:=false endend;procedure getreadyforsearch(var sys:tsearchsystem);var i,j:integer;begin with sys do begin f:=0;g:=0; fillchar(number,sizeof(number),0); fillchar(cut,sizeof(cut),0); for i:=1 to n do for j:=1 to n div i do inc(numberi*j); fillchar(match,sizeof(match),0); repeat until not
18、 find(sys,true); end;end;function can(dep,d1,d2:integer):boolean;var i:integer;begin with searchsysdep do begin if d1 mod d2=0 then begin can:=false;exit end; for i:=1 to d2 shr 1 do if not cuti and (d2 mod i=0)and(d1 mod i0) then begin can:=true;exit end; can:=false endend;procedure search(dep:inte
19、ger);var i,j,k,t,best:integer; b:boolean;begin if getso then exit; with searchsysdep do begin if f=lower then begin getso:=true;ans:=nowans;tans:=dep;exit end; if f+g=2) then begin if (best0) and (ibest) then continue; if numberi=4 then begin b:=true; for j:=1 to i shr 1 do if (i mod j=0) and not cu
20、tj and (leveljleveli-1) then begin b:=false;break end; if not b then continue; end; if (dep0)and(inowansdep-1)and(nowansdep-10)and can(dep-1,i,nowansdep-1) then continue; if best0 then nowansdep:=-i else nowansdep:=i; searchsysdep+1:=searchsysdep; with searchsysdep+1 do begin inc(f,i); for j:=1 to i
21、 do if (i mod j=0) and not cutj then begin cutj:=true; for k:=2 to n div j do dec(numberj*k) end; for j:=1 to i do if (i mod j=0) and (matchj0) then begin if matchjj then t:=matchj else t:=j; dec(g,t); matchmatchj:=0;matchj:=0 end; end; repeat until not find(searchsysdep+1,true) and not find(searchs
22、ysdep+1,false); search(dep+1); end endend;procedure work;var i:integer;begin getreadyforsearch(searchsys0); lower:=searchsys0.g+1; getso:=false; repeat dec(lower); search(0); until getso; assign(output,outputfile);rewrite(output); writeln(lower); close(output);end;begin assign(input,inputfile);reset(input);readln(n);close(input); prepare; work;end.
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1