upd:250920 注:紫书中所说的第三种方法有问题,弃用。原judge-uva12105没有考虑前导0问题,已更新自己的数据到洛谷题目上。
以下是紫书中的第二种方法。
dp[i][j]表示拼了i位数,余数是j时,需要的最少火柴棒数量。
获得答案的时候,从高位到低位,每一位从9到0遍历,尽量挑大的使用,注意答案不能有前导0,例如0003,0000,可以是0。
#include<iostream>
#include<cstdio>
using namespace std;
#define inf 105
const int maxn=105,maxm=3005;
int dp[maxn][maxm],r[maxn][10];
int n,m,kase;
int need[15]={6,2,5,5,4,5,6,3,7,6};
int main()
{
//freopen("expin.txt","r",stdin);
//freopen("expout3.txt","w",stdout);
ios::sync_with_stdio(false);
cin.tie(nullptr);
while(cin>>n>>m && n)
{
cout<<"Case "<<++kase<<": ";
//pre-compute r array
for(int i=1;i<=9;i++) r[1][i]=i%m;
for(int i=2;i<=n;i++)
{
for(int d=1;d<=9;d++)
{
r[i][d]=(r[i-1][d]*(10%m))%m;
}
}
//initialize dp array
for(int i=0;i<=n;i++) {
for(int j=0;j<m;j++) dp[i][j]=inf;
}
dp[0][0]=0;
for(int i=0;i<=n;i++){
for(int j=0;j<m;j++){
for(int d=9;d>=0;d--){
int &ans=dp[i+1][(j*10+d)%m];
ans=min(ans,dp[i][j]+need[d]);
}
}
}
int i;
for(i=n;i>0;i--) if(dp[i][0]<=n) break;
if(i==0) cout<<-1<<"\n";
else{
int t=0;
bool leading_zero=1;
for(;i>=1;i--){
int d;
for(d=9;d>=0;d--)
{
int nxt=(t-r[i][d]+m)%m;
if(dp[i-1][nxt]+need[d]<=n)
{
n-=need[d];
t=nxt;
break;
}
}
if(i>1 && d==0 && leading_zero==1) continue;
leading_zero=0;
cout<<d;
}
if(leading_zero==1) cout<<-1;
cout<<"\n";
}
}
return 0;
}