【Nov 8 P1,再一次DP】符文之语
【题目描述】
当小 FF来到神庙时,神庙已经破败不堪了。但神庙的中央有一个光亮如新的石台。小 FF走近石台,发现石台上有一个数串,而数串的上方刻着一串古老的符文之语。精通古符文之语的小 FF不费吹灰之力就读懂了文章的意思,其大意是:对于石台上的一串数字,你可以在适当的位置加入乘号(设加了 k个,当然也可不加,即分成k+1个部分),设这k+1个部分的乘积(如果k=0,则乘积即为原数串的值)对m的余数(即 mod m)为 x;现求 x能达到的最小值及该情况下k的最小值,以及 x能达到的最大值及该情况下的k的最小值(可以存在 x的最小值与最大值相同的情况)。
小 FF还知道,如果他找到了正确的答案,那么就可以通往神庙的下层了。但这个问题似乎不太好解决,小 FF就找到了你,并答应找到财宝以后和你二八分(当然你拿二……)。
【输入格式】
第一行为数串,且数串中不存在 0;
第二行为 m.
【输出格式】
四个数,分别为 x的最小值和该情况下的 k,以及 x的最大值和该情况下的 k,相邻
两个数之间用一个空格隔开。
【输入样例】
4421
22
【输出样例】
0 1 21 0
【数据范围】
对于 30%的数据: 2 <=字符串长度 L<=50.
对于100%的数据: 2 <=字符串长度 L<=1000; 2<=m<=50.
这道题看上去就像是乘积最大那道题,但是由Mod 4最小那题我们知道,单纯的打乘积最大那样的DP是绝对不对的。
我们用f[i,j]表示前i位通过添加乘号取得余数j所需划分的的最小部分数(也就是添加的乘号数加1)。这样就得到方程f[i,k*d[j+1,i]]=min{f[j,k]}+1(i>j)。其中,d[i,j]数组表示串中i位到j位的数mod m的结果。
参考代码:
program chars; const maxn=32767; var a:array[0..1010]of integer; d:array[0..1010,0..1010]of integer; f:array[0..1010,0..1010]of integer; v:array[0..50]of boolean; ch:char; i,j,p,mm:longint; n,m,k,sum:longint; begin read(ch); while (ord(ch)>=48)and(ord(ch)<=57)do begin inc(n); a[n]:=ord(ch)-48; read(ch); end; readln(m); for i:=1 to n do for j:=i to n do d[i,j]:=(d[i,j-1]*10+a[j])mod m; for i:=0 to n+1 do for j:=0 to m do f[i,j]:=maxn; //求最小值,不要忘了初始化 fillchar(v,sizeof(v),false); f[0,1]:=0; for i:=1 to n do for j:=0 to i-1 do for k:=0 to m-1 do if f[j,k]+1<f[i,k*d[j+1,i] mod m] then begin f[i,k*d[j+1,i] mod m]:=f[j,k]+1; v[k*d[j+1,i] mod m]:=true; end; for i:=0 to m-1 do if f[n,i]<>maxn then begin write(i,' ',f[n,i]-1,' '); break; end; for i:=m-1 downto 0 do if f[n,i]<>maxn then begin write(i,' ',f[n,i]-1,' '); break; end; end.
BTW,Yanni的With an Orchid的确不错,值得一听!
本文地址:http://www.cnblogs.com/saltless/archive/2010/11/09/1872277.html
(saltless原创,转载请注明出处)