【问题描述】
你的任务是用不超过n(n≤100)根火柴摆一个尽量大的,能被m(m≤3 000)整除的正整数,如图1-64所示。例如n=6和m=3,解为111。无解时输出-1。如下图所示:数字0到9,它们的火柴数量分别是:6、2、5、5、4、5、6、3、7、6。
【输入格式】
若干行,每行包含两个正整数n和m。输入以n=0结束。
【输出格式】
若干行,对应输入的n和m。
【输入样例】
6 3
5 6
0
【输出样例】
111
-1
【数据范围】
对于20%的数据:1<=n<=20
对于50%的数据:1<=n<=30
对于100%的数据:1<=n<=100,1<=m<=3000。每个测试点最多不超过10组数据。
题解:
白书上的题解是记录当前对m取余为j的位数为i所需要的最少火柴数,这里不去管它,我要讲的是更暴力也更容易想到的状态函数设计:
f(i,j)表示用i根火柴能组成的被m取模为j的最大值
设p[k]为组成k需要的火柴数,则有
f(i,j)=max{ f(i-p[k],(j-k) mod m) | 0<=k<=9 && i-p[k]>=0 }
很明显最终结果远远超过了long long 的数据范围,需要用到高精度运算
题目本身不太难,主要是在写高精度时有一些小地方需要注意
代码实现:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll; //0,1,2,3,4,5,6,7,8,9
const int mo=1000000,p[]={6,2,5,5,4,5,6,3,7,6};
//压一压位,优化下计算速度
struct bign{
int z[35],len;
bign(){
memset(z,0,sizeof(z));
len=1;
}
//最开始用的sprintf将整数转化成字符串再赋值,但显而易见太慢了
void operator=(int x){
memset(z,0,sizeof(z));
len=0;
if(x==0) len=1;
else while(x!=0) z[len++]=x%mo,x/=mo;
}
friend bool operator<(bign a,bign b){
if(a.len!=b.len) return a.len<b.len;
for(int i=a.len-1;i>=0;i--) if(a.z[i]!=b.z[i]) return a.z[i]<b.z[i];
return 0;
}
//因为题目只涉及到加法和乘法,所以只重定义了这两个运算符
friend bign operator+(bign a,int b){
bign c=a;
int len=a.len;
c.z[0]+=b;
for(int i=0;i<len;i++) c.z[i+1]+=c.z[i]/mo,c.z[i]%=mo;
while(c.z[len]>0) c.z[len+1]+=c.z[len]/mo,c.z[len]%=mo,len++;
while(len>1&&c.z[len-1]==0) len--;
c.len=len;
return c;
}
friend bign operator*(bign a,int b){
bign c;
int len=a.len+15;
for(int i=0;i<a.len;i++) c.z[i]+=a.z[i]*b;
for(int i=0;i<len;i++) c.z[i+1]+=c.z[i]/mo,c.z[i]%=mo;
while(c.z[len]>0) c.z[len+1]+=c.z[len]/mo,c.z[len]%=mo,len++;
while(len>1&&c.z[len-1]==0) len--;
c.len=len;
return c;
}
void out(){
printf("%d",z[len-1]);
for(int i=len-2;i>=0;i--) printf("%06d",z[i]);
printf("\n");
}
}d[105][3005];
int n,m;
bool vis[105][3005]={0};
inline int in(){
int x=0,f=1;char ch=getchar();
while(ch<'0'|ch>'9'){if(ch=='-') f=-f;ch=getchar();}
while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=getchar();
return x*f;
}
int main(){
bign tmp1,tmp2,ans;
while(1){
n=in();
if(n==0) break;
memset(vis,0,sizeof(vis));
m=in(),ans.z[0]=-1,ans.len=1;
for(int k=1;k<=9;k++){
tmp1=k;
if(d[p[k]][k%m]<tmp1) d[p[k]][k%m]=tmp1;
vis[p[k]][k%m]=1;
}
for(int i=1;i<=n;i++){
for(int j=0;j<m;j++){
if(!vis[i][j]) continue;
tmp1=d[i][j]*10;
for(int k=0;k<=9;k++){
if(i+p[k]>n) continue;
tmp2=tmp1+k;
if(d[i+p[k]][(j*10+k)%m]<tmp2) d[i+p[k]][(j*10+k)%m]=tmp2;
vis[i+p[k]][(j*10+k)%m]=1;
}
}
if(vis[i][0]) if(ans<d[i][0]) ans=d[i][0];
}
ans.out();
for(int i=0;i<=n;i++) for(int j=0;j<m;j++) d[i][j]=0;
}
return 0;
}