UVA12105 Bigger is Better
题目描述:紫书P293
用不超过 n(1≤n≤100)n(1 \leq n \leq 100)n(1≤n≤100) 根火柴摆出一个尽量大的、能被 m(1≤m≤3000)m(1 \leq m \leq 3000)m(1≤m≤3000) 整除的数。

解题思路
方法一:
来自紫书,设dp(i,j)dp(i,j)dp(i,j)表示用iii根火柴能拼出除以mmm余数为jjj的最大的数,枚举在最右边添加数字kkk,用dp(i,j)∗10+kdp(i,j)*10+kdp(i,j)∗10+k去更新dp(i+c(k),(j∗10+k)%m)dp(i+c(k),(j*10+k)\%m)dp(i+c(k),(j∗10+k)%m),其中c(k)c(k)c(k)表示数字kkk需要的火柴数。但是由于最多能有50位,需要高精度。
方法二:
也来自紫书,dp(i,j)dp(i,j)dp(i,j)表示拼出一个“除以mmm余数为jjj的iii位数”至少需要多少根火柴,状态转移和上面差不多,我们也枚举最后一位kkk,用dp(i,j)+c[k]dp(i,j)+c[k]dp(i,j)+c[k]去更新dp(i,(j∗10+k)dp(i,(j*10+k)%m)dp(i,(j∗10+k)即可。
这时我们知道了答案是几位数,那么接下来从高位开始依次输出,因为要让这个数尽量大,所以对于每一位我们都按9,8,7…降序依次判断是否可行,例如m=7m=7m=7,并已经确定了最大位数为3位数。首先试着让最高位为9,如果能摆出形如9ab的整数,那它一定数最大的,又因为900除以7的余数为4,所以后两位(ab)除以7的余数应该为3,如果d(2,3)+c(9)≤nd(2,3)+c(9) \leq nd(2,3)+c(9)≤n即可。
如何计算出x00…除以mmm的余数需要预处理,还要注意注意最高位不能为0。
方法三:
设dp(i,j)dp(i,j)dp(i,j)表示用不超过iii根火柴摆成“还需要剩余部分除以mmm余数为jjj”的最大位数(当前余数是m-j)。
状态转移就是dp(i,j)=max(dp[i−c[k]][(j∗10+k)%m]),(0≤k≤9)dp(i,j)=max(dp[i-c[k]][(j*10+k)\%m]),(0\leq k \leq9)dp(i,j)=max(dp[i−c[k]][(j∗10+k)%m]),(0≤k≤9)(自己想下其实还是很好理解的和方法二的差不多)。为了方便输出,再定义一个p(i,j)p(i,j)p(i,j)表示dp(i,j)dp(i,j)dp(i,j)状态时的最大位的数是多少,边界条件dp(j,0)=0dp(j,0)=0dp(j,0)=0,如果我当前的数已经符合条件,那么我可以不再加,也就是余数为0时,长度为0,因为dp(i,j)dp(i,j)dp(i,j)中的iii表示的是不超过iii,表示即使我不在再添加数字了也可以有多出来的火柴,这时dp(j,0)dp(j,0)dp(j,0)的jjj表示的就是哪些多出来的火柴。(如果dp(i,j)dp(i,j)dp(i,j)的iii表示的是刚好用iii根火柴,那么只需要dp(0,0)=0dp(0,0)=0dp(0,0)=0)。
让dp(i,j)dp(i,j)dp(i,j)表示剩余iii根(已经用了n−in-in−i根)余数为jjj的最大位数也是一样的,不过是从高位开始枚举,有兴趣的可以试试。
代码快
方法一大数没去做,方法二写的有点乱,没整理,这里只提供方法三的
#include <bits/stdc++.h>
using namespace std;
int dp[105][3005];
int p[105][3005];
int c[]={6,2,5,5,4,5,6,3,7,6};
int main(){
int n,m,cas=0;
while(cin>>n&&n)
{
cin>>m;
memset(dp,-1,sizeof(dp));
for(int i=0;i<=n;i++)
dp[i][0]=0;
int ans=-1,x,y;
for(int i=1;i<=n;i++)
for(int j=0;j<m;j++)
for(int k=9;k>=0;k--)
if(i>=c[k])
{
if(dp[i-c[k]][(j*10+k)%m]>=0&&dp[i][j]<(dp[i-c[k]][(j*10+k)%m]+1))
{
dp[i][j]=dp[i-c[k]][(j*10+k)%m]+1;
p[i][j]=k;
}
}
ans=dp[n][0];
printf("Case %d: ",++cas);
if(ans<=0){
printf("-1\n");continue;
}
x=n;y=0;
while(ans--)
{
int e=p[x][y];
printf("%d",e);
x-=c[e];
y=(y*10+e)%m;
}
printf("\n");
}
return 0;
}

博客详细解析了UVA12105 Bigger is Better的解题思路,包括三种方法:方法一使用高精度计算,方法二优化状态转移,方法三调整状态定义。所有方法都基于动态规划,目标是用有限火柴拼出能被特定数整除的最大数。

1545

被折叠的 条评论
为什么被折叠?



