解题报告5整数因子团问题.docx

上传人:b****6 文档编号:5810977 上传时间:2023-01-01 格式:DOCX 页数:14 大小:68.03KB
下载 相关 举报
解题报告5整数因子团问题.docx_第1页
第1页 / 共14页
解题报告5整数因子团问题.docx_第2页
第2页 / 共14页
解题报告5整数因子团问题.docx_第3页
第3页 / 共14页
解题报告5整数因子团问题.docx_第4页
第4页 / 共14页
解题报告5整数因子团问题.docx_第5页
第5页 / 共14页
点击查看更多>>
下载资源
资源描述

解题报告5整数因子团问题.docx

《解题报告5整数因子团问题.docx》由会员分享,可在线阅读,更多相关《解题报告5整数因子团问题.docx(14页珍藏版)》请在冰豆网上搜索。

解题报告5整数因子团问题.docx

解题报告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。

一个游戏过程是:

步骤②:

选择整数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所选数字的次序对游戏的得分有很大影响。

应该如何选择才能使游戏获得最大得分?

2≤n≤120。

如n=6时的解为15;n=15时的解为81;n=30时的解为301;n=50时的解为808;n=65的解为1328。

2、算法分析。

看看n=6时的例子,我们很容易得到答案。

再大一点呢?

例如n=15时,还能很快得到81的解吗?

也不是很难,因为人在玩游戏的过程中容易得出一个比较优的决策。

在数据比较小时,还能够比较快的得到最优决策。

这是因为我们在用一个策略——贪心策略!

的确,由于题目的简明清晰,很容易想出各种贪心方法。

这些贪心方法,有的能够保证最优,有的却不能保证。

但是,这是一道求最优解的题。

贪心的思想只能作为一个参考,并不能解决此题。

弄了很久,很难找到比较好的方法,最后,搜索似乎是唯一的出路。

搜就搜吧。

看到数据范围很容易受惊,太大了!

要能够高效率,就必须有很强的剪枝。

在一般情况下,如果求出了一个下界,用最优性剪枝可能会比较有效果。

这个下界,可以首先贪心得到,也可以在搜索过程中贪心的规定搜索顺序。

但是,这个是最简单的剪枝,它还远远不够。

最优性剪枝的判断方法是,如果当前得到的分数加上后面最多能够得到的分数还不超过现在的最优解,那么就可以剪枝。

“后面最多能够得到的分数”——这意味着我们只求出下界是不够的,还需要求出上界。

这个上界看起来不是很好求,就用剩下的所有数的和代替?

太不像样了。

要得到一个像样的上界,就必须分析这堆数的规律。

首先,把这些数和它们的关系用图的方法表现出来。

用n个节点代表n个数,如果a是b的倍数,那么就从a到b连一条有向边。

显然,这个倍数关系是一个偏序关系,由于它的传递性,可以去掉一些边,使得从剩下的边就可以看出一个偏序关系。

如果a是b的倍数,存在b

如果a/b是一个质数,那么a,b之间的边就会保留,自己到自己的边也可以忽略掉。

结论①:

如果a是可以被选的数,那么a必然有一个没有删掉的约数b,a/b是质数。

证明:

如果a被删除了,那么a的约数也都被删除了。

假设a有约数b没有被删除,但没有一个满足a/b是质数。

那么,必然有数c,使得c是a的约数,b是c的约数,并且a/c是质数。

由于c被删除了,所以b也应该被删除了,所以假设不成立。

每一次删除时,至少都要删除两个数,求上界时,考虑最好的情况:

每次都只删除两个数。

由于每一个数最多能删除一次,所以不难得出一个模型:

在图中求出一个最佳匹配!

具体模型就是,把原来构造的图看成是无向图,边上的权值定为它两个顶点中数字较大的那一个,求一个最佳匹配,匹配表示选择这条边上较大的数删除,较小的数也被删除了。

这是一个理想情况,也就是一个上界。

怎样求最佳匹配呢?

进一步发现,上面的图是一个二分图:

只要我们对每一个结点标记一个层次:

一个节点u,把u分解质因数,设

,其中pi是互不相同的质数,结点u的层次level(u)就是:

a1+a2+……+ak。

那么,一条边连接的两个点u,v一定满足|level(u)-level(v)|=1。

这样,把level值为奇数的点作为点集X,为偶数的作为点集Y,这样就得到了一个二分图的模型。

最佳匹配可以用找增广轨的方法。

顺便说一句,这个上界会不会就是答案呢?

不是的,例如n=50时求出上界是811,但实际答案是808。

因为求匹配时存在着这样的匹配:

a)2—22,b)3—39,c)11—33,d)13—26。

不难发现,由于删除22时2在前面没有被删除,所以删除操作a)一定在d)之前,同理得出b)在c)之前,c)在a)之前,d)在b)之前。

这样就形成了一个环:

这个匹配不能得出一个删除方案。

即使如此,在数据较小时这个上界基本上是答案,有的虽然不是答案,也只比答案大一点了,这说明这个上界已经非常好了,那么就可以尝试用这个上界。

求上界的方法确定了,再来确定一个搜索方案。

在这里,可以考虑用IDA*算法。

对于一个状态,f表示这个状态现在已经得到的分数,g函数就是在剩下的点中匹配得出的一个上界,现在证明函数h=f+g满足相容性。

(不同于求最小值,这里需要求最大值,所以需要证明state1.h≥state2.h,其中state2是state1的后继)。

证明:

设state2是通过state1删除数a得到的,那么state2.f=state1.f+a。

那么必然有一个a的约数b,在state1中没有被删除,而在state2中删除了。

state2不会有a,b点,只需要在state2的最佳匹配中,加上一个匹配a--b,这样可以得到一个state1的匹配,它的权值和是state2.g+a,所以state1.g≥state2.g+a。

所以state1.f≥state2.f,这样就证明了相容性。

这样就可以尝试着用IDA*算法来实现了,但实践证明效果不是很好。

原因是状态总数还是很多,还需要做一些优化。

结论②:

如果一个数a在删除时,它有大于等于4个约数(包含自己)没有删除,并且它至少有一个层次比它小2的约数没有删除,那么删除a不会是最优的方案。

证明:

由于a剩下的约数在图中是连通的,所以可以分情况讨论(这些情况有交集但覆盖了所有的情况)。

设a有k个约数。

情况a):

如果有大于等于2小于等于k-2个约数满足它的层次比a的层次小1。

那么,至少存在一个层次为level(a)-2的约数,它到某一个层次为level(a)-1的约数b有边。

那么可以删除b,由于a有至少两个层次为level(a)-2的点,所以删除b后,a仍然能够被选。

这时,先删除b再删除a剩下的数与直接删除a剩下的数相同,但得到的分数比直接删除a好。

情况b):

如果有一个层次为level(a)-3的约数,那么从a出发有一个长度为3的链,这时,设这条链是a->b->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,显然p1≠p2并且p1,p2都是质数,那么考虑a/p1他一定是c的倍数。

由结论①知a/p1一定没有被删除。

同样a/p2也没有被删除,但是却只有一个层次为level(a)-1的点,这显然不可能。

即情况c)不会出现。

不难发现上面已经包含了所有的情况。

这个可以用来剪枝。

另外,在有些情况下能够保证最优,例如:

猜想③:

一个数只有两个约数没有被删除,并且这个数的没有倍数(除了自己),并且它是满足前面条件的最大的数,那么删除这个数是最优的。

我不能证明猜想,但是可以证明的是,在n≤120时不会有反例,而在n较大时似乎有一个这样的例子,这个只是在证明时构造出来的一个反例,实际中也许不会出现,下图表示。

然后,有些删除操作可以交换,即顺序不同但得到的分数和剩下的状态是一样的。

为了避免重复,可以考虑相邻两个操作能不能交换的情况。

还有,在求最大匹配的时候,不需要每次都重新找增广轨,只需要在以前找到匹配的基础上修改,这个修改,不需要是增广轨。

它是一条匹配便于没有匹配边的交替轨,它可以是偶数条边,只需要改变的权值大于0就可以了。

至此,算法已经基本成形了。

如果用上上面所有的优化,包括没有证明的猜想③,实践证明效果非常好。

下面是一些测试的结果:

N

10

30

50

65

80

100

105

110

120

答案

40

301

808

1328

2041

3164

3434

3764

4593

时间(单位:

s)

0.00

0.00

0.00

0.00

0.00

0.05

0.36

0.22

0.22

测试环境:

Pentium41.70GHzCPUFreepascalv1.0.6。

所有的数据加起来都只用了2.14s。

奇怪的是,如果不用猜想③,那么在n从70到74,81到86,和从105到120这一段速度奇慢,其余的都是很快,下面的数据可以说明:

n

70

71

72

73

74

81

82

83

84,85,105~120

答案

1566

1570

1642

1644

1718

2105

2187

2191

/

时间(不用猜想)

110.99

116.29

51.92

66.76

154.89

42.42

89.45

95.22

Toolong

除了没有运行出来的点不知道外,其余的点都没有找到猜想③的反例。

[参考文献]

《算法艺术与计算机竞赛》——刘汝佳黄亮

[讨论]

向刘汝佳请教过,知道题目还没有找到多项式算法,然后从他书上的提示找算法。

[感谢]

刘汝佳

3、程序

const

maxn=120;

maxnode=500;

size=127;

inputfile=’taxman.in’;

outputfile=’taxman.out’;

type

tsearchsystem=record

f,g:

longint;

match,number:

array[1..maxn]ofinteger;

cut:

array[1..maxn]ofboolean;

end;

var

getso:

boolean;

n,tans:

integer;

lower:

longint;

point,level:

array[1..maxn+1]ofinteger;

node,value:

array[1..maxnode]ofinteger;

searchsys:

array[0..maxnshr1]oftsearchsystem;

ans,nowans:

array[0..maxnshr1]ofinteger;

procedureprepare;

var

i,j,p:

integer;

prime:

array[1..maxn]ofboolean;

begin

fillchar(prime,sizeof(prime),1);

fori:

=2tondoifprime[i]then

forj:

=itondividoprime[i*j]:

=false;

level[1]:

=0;

fori:

=2tondoforj:

=2toidoifimodj=0thenbegin

level[i]:

=level[idivj]+1;

break

end;

p:

=0;

fori:

=1tondobegin

point[i]:

=p+1;

forj:

=1toi-1doif(imodj=0)andprime[idivj]thenbegin

inc(p);node[p]:

=j;value[p]:

=i

end;

forj:

=2tondividoifprime[j]thenbegin

inc(p);node[p]:

=i*j;value[p]:

=i*j

end

end;

point[n+1]:

=p+1;

end;

functionfind(varsys:

tsearchsystem;direct:

boolean):

boolean;

var

i,k,head,tail,t:

integer;

q:

array[0..size]ofinteger;

inq:

array[1..maxn]ofboolean;

dist,par:

array[1..maxn]ofinteger;

begin

withsysdobegin

head:

=0;tail:

=0;

fillchar(par,sizeof(par),0);

fillchar(inq,sizeof(inq),0);

fori:

=1tondo

if(match[i]=0)and(odd(level[i])=direct)andnotcut[i]thenbegin

inc(tail);q[tail]:

=i;dist[i]:

=0;inq[i]:

=true;

end

elsedist[i]:

=-maxint;

whilehead

inc(head);

k:

=q[headandsize];

inq[k]:

=false;

ifodd(level[k])=directthenbegin

fori:

=point[k]topoint[k+1]-1do

if(node[i]<>match[k])andnotcut[node[i]]thenbegin

t:

=node[i];

ifcut[t]thencontinue;

ifdist[k]+value[i]>dist[t]thenbegin

dist[t]:

=dist[k]+value[i];

par[t]:

=k;

ifnotinq[t]thenbegin

inc(tail);

q[tailandsize]:

=t;

inq[t]:

=true

end

end

end

end

elsebegin

ifmatch[k]<>0thenbegin

ifmatch[k]>kthent:

=match[k]elset:

=k;

ifdist[k]-t>dist[match[k]]thenbegin

dist[match[k]]:

=dist[k]-t;

par[match[k]]:

=k;

ifnotinq[match[k]]thenbegin

inc(tail);

q[tailandsize]:

=match[k];

inq[match[k]]:

=true

end

end

end

end

end;

t:

=-maxint;

fori:

=1tondo

if(odd(level[i])xordirect)and(dist[i]>t)and(match[i]=0)andnotcut[i]then

begin

k:

=i;t:

=dist[i]

end;

ift>0thenbegin

find:

=true;

inc(g,t);

whilek<>0dobegin

i:

=par[k];

match[i]:

=k;match[k]:

=i;

k:

=par[i]

end;

exit

end;

t:

=-maxint;

fori:

=1tondo

if(odd(level[i])=direct)and(dist[i]>t)andnotcut[i]then

begin

k:

=i;t:

=dist[i]

end;

ift>0thenbegin

find:

=true;

inc(g,t);

i:

=match[k];

match[k]:

=0;

whilei<>0dobegin

k:

=par[i];

match[k]:

=i;match[i]:

=k;

i:

=par[k]

end

end;

find:

=false

end

end;

proceduregetreadyforsearch(varsys:

tsearchsystem);

var

i,j:

integer;

begin

withsysdobegin

f:

=0;g:

=0;

fillchar(number,sizeof(number),0);

fillchar(cut,sizeof(cut),0);

fori:

=1tondoforj:

=1tondivido

inc(number[i*j]);

fillchar(match,sizeof(match),0);

repeatuntilnotfind(sys,true);

end;

end;

functioncan(dep,d1,d2:

integer):

boolean;

var

i:

integer;

begin

withsearchsys[dep]dobegin

ifd1modd2=0thenbegincan:

=false;exitend;

fori:

=1tod2shr1do

ifnotcut[i]and(d2modi=0)and(d1modi<>0)thenbegin

can:

=true;exit

end;

can:

=false

end

end;

proceduresearch(dep:

integer);

var

i,j,k,t,best:

integer;

b:

boolean;

begin

ifgetsothenexit;

withsearchsys[dep]dobegin

iff=lowerthenbegin

getso:

=true;ans:

=nowans;tans:

=dep;exit

end;

iff+g

best:

=0;

fori:

=ndownto1doifnotcut[i]and(number[i]=2)thenbegin

b:

=true;

forj:

=2tondividoifnotcut[i*j]thenb:

=false;

ifbthenbeginbest:

=i;breakend;

end;

fori:

=ndownto1do

ifnotcut[i]and(number[i]>=2)thenbegin

if(best<>0)and(i<>best)thencontinue;

ifnumber[i]>=4thenbegin

b:

=true;

forj:

=1toishr1do

if(imodj=0)andnotcut[j]and(level[j]<>level[i]-1)then

beginb:

=false;breakend;

ifnotbthencontinue;

end;

if(dep>0)and(i>nowans[dep-1])and(nowans[dep-1]>0)and

can(dep-1,i,nowans[dep-1])then

continue;

ifbest<>0thennowans[dep]:

=-ielsenowans[dep]:

=i;

searchsys[dep+1]:

=searchsys[dep];

withsearchsys[dep+1]dobegin

inc(f,i);

forj:

=1toidoif(imodj=0)andnotcut[j]thenbegin

cut[j]:

=true;

fork:

=2tondivjdodec(number[j*k])

end;

forj:

=1toidoif(imodj=0)and(match[j]<>0)thenbegin

ifmatch[j]>jthent:

=match[j]elset:

=j;

dec(g,t);

match[match[j]]:

=0;match[j]:

=0

end;

end;

repeatuntilnotfind(searchsys[dep+1],true)and

notfind(searchsys[dep+1],false);

search(dep+1);

end

end

end;

procedurework;

var

i:

integer;

begin

getreadyforsearch(searchsys[0]);

lower:

=searchsys[0].g+1;

getso:

=false;

repeat

dec(lower);

search(0);

untilgetso;

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