您的当前位置:首页正文

乘积最大

2020-07-04 来源:步旅网
回专题模式 回学习阶段模式 【题目名称、来源】

乘积最大noip2003t2 chengji.pas 【问题描述】

今年是国际数学联盟确定的“2000——世界数学年”,又恰逢我国著名数学家华罗庚先 生诞辰90周年。在华罗庚先生的家乡江苏金坛,组织了一场别开生面的数学智力竞赛的活 动,你的一个好朋友XZ也有幸得以参加。活动中,主持人给所有参加活动的选手出了这样 一道题目:

设有一个长度N的数字串,要求选手使用K个乘号将它分成K+1个部分,找出一种 分法,使得这K+1个部分的乘积能够为最大。

同时,为了帮助选手能够正确理解题意,主持人还举了如下的一个例子: 有一个数字串: 312,当N=3,K=1时会有以下两种分法: 1)3*12=36 2)31*2=62

这时,符合题目要求的结果是: 31*2=62

现在,请你帮助你的好朋友XZ设计一个程序,求得正确的答案。 输入:

程序的输入共有两行:

第一行共有2个自然数N,K (6<=N<=40,1<=K<=6) 第二行是一个长度为N的数字串。 输出:

结果显示在屏幕上,相对于输入,应输出所求得的最大乘积(一个自然数)。 样例: 输入 4 2 1231 输出 62

【所属专题】

【适合学习阶段】

【解题思路】 问题分析:

很明显的动态规划。

{ps:考虑到*所加的位置不同,可以导致数字串被分开的位置不同,子串的长度不同,类似于对于数字串加括号,所以应该枚举所有长度l,所有起始位置i的子串,所以使用二位数组d[i,j]存储,长度为0的子串d值为0,长度为1的子串d值为该位数字。

因为要在数字串中加入k个*,考虑如果加入的*大于等于数字串长度,即j-kD[i,j] 当 K=0,s=1231 1 12 123 1231 0 2 23 231 0 0 3 31 0 0 0 1 D[I,j] 当k=1,s=1231 0 2 36 372 0 0 6 62 0 0 0 3 0 0 0 0 D[I,j] 当k=2,s=1231 0 0 6 62 0 0 0 6 0 0 0 0 0 0 0 0 } 令d[i,j,k]为第i个数字到第j个数字加k个乘号所能够达到的最大值。 状态转移方程是:

d[i,j,k]=max{num[i,t]*d[t+1,j,k-1]} (枚举t, 满足i<=t<=j-1)

{ps:状态转移方程规定所加的第k个*必须在第k-1个*左边的数字串中,因为*没有顺序要求,加在数字串左右都是一样的,因为d[t+1,j,k-1]就是枚举右边加k-1个*后的结果。}

注意到状态转移的时候总是使k减小(或不变)的,所以把k作为阶段来递推(节约空间)。

在每个状态中,l=j-i越来越小,所以从l=0递推到l=n 即:

for k:=1 to c do for l:=0 to n do for i:=1 to n do 递推d[i,j,k]

{ps:使用l即数字串长度从大到小作为数字串的枚举变量,可以根据i计算出来相应的j,达到了枚举i,j的效果}

显然,用两个数组记录d[i,j,k]和d[i,j,k-1],就可以只用两维:d[i,j] 于是算法框架就是(请对照我的源程序):

{ps:不能看成类似于“邮局”的动态规划,即设c[i,j]代表从1..j的数字串中放i个*,第i个*恰放于j之前的最大乘积值。但是这样不行因为有后效性 例如对于输入 4 2 1231

C[i,j]如下图所示 0 1*2 12*3 123*1 0 0 1*2*3 后效性 }

初始化d1[i,j]

for k:=1 to c do begin

for l:=0 to n do for i:=1 to n do

用d1[i,j](k-1阶段)递推d2[i,j](k阶段)

d1:=d2; {节省空间,因为递推的时候只与上个阶段有关,故只保留相邻两个阶段} end;

高精度乘法的方法我不是用的模拟手算的过程(这个大家会做吧),而用了类似 多项式乘法的方法,因为我觉得这种写法很好记!(大家仔细看看我的mul过程)

程序在此: const

maxn=40;

ch:array[0..9] of char='0123456789';

v:array['0'..'9'] of integer=(0,1,2,3,4,5,6,7,8,9); type

pbign=^bign;

bign=string[maxn]; var

s:string;

i,j,k,l,n,c,t:integer; x,y:integer; r:bign;

d1,d2:array[1..maxn,1..maxn] of pbign;

procedure mul(a,b:bign; var c:bign); {useful procedure} var

tmp:array[1..maxn*2] of integer; i,j,g,p,k:integer; begin

{init tmp}

while (length(a)>1)and(a[1]='0') do delete(a,1,1); while (length(b)>1)and(b[1]='0') do delete(b,1,1);

p:=length(a)+length(b); for i:=1 to p do tmp[i]:=0;

for i:=1 to length(a) do

for j:=1 to length(b) do {calc like poly!} begin

k:=p-i-j; {how many '0's}

inc(tmp[p-k] ,v[a[i]]*v[b[j]] mod 10); {add here!} inc(tmp[p-k-1],v[a[i]]*v[b[j]] div 10); end;

{update tmp} g:=0; c:='';

for i:=p downto 1 do begin

c:=ch[(tmp[i]+g) mod 10]+c; g:=(tmp[i]+g) div 10; end;

while (length(c)>1)and(c[1]='0') do delete(c,1,1); end;

function bigger(a,b:bign):boolean; var

i:integer; begin

bigger:=true;

if length(a)>length(b) then exit; bigger:=false;

if length(a)b[i] then begin

bigger:=true; exit; end; end;

begin

readln(n,c); readln(s);

for i:=1 to n do for j:=i to n do begin

new(d1[i,j]); new(d2[i,j]);

d2[i,j]^:=copy(s,i,j); end;

for k:=1 to c do

begin

for l:=0 to n do for i:=1 to n do begin j:=i+l;

if j>n then break; d1[i,j]^:='0';

{where to add first '*': t}

for t:=i to j-k do {if t>j-k, there will be too many '*'s! } begin

mul(copy(s,i,t-i+1),d2[t+1,j]^,r); if bigger(r,d1[i,j]^) then d1[i,j]^:=r; end; end;

for i:=1 to n do for j:=1 to n do

d2[i,j]^:=d1[i,j]^; {we use pointers so don't use d2:=d1} end;

writeln(d1[1,n]^); end.

存储结构: 【测试数据】

【源程序】

因篇幅问题不能全部显示,请点此查看更多更全内容