Big is Better
题目大意:用火柴拼数字,每个数字需要的火柴如下图,共有n(n<=100)个火柴,要求拼出的数能整除m(m<=3000),求所能拼出的最大的数字是多少。
思路:按位进行DP,dp[i][j]
表示用i位数表示出除以m余j的数最少需要多少根火柴棒,如果不存在则为inf,最后得到最大的i满足dp[i][0]
存在,则最大位数为i,令high=i
然后从第high位开始枚举,先从9开始,如果填9可以,等价于dp[high-1][(m-9xxxxx)%m]+f[9]<=n
,相当于这一位先用掉了f[9]=6
根火柴,再用剩下的火柴一定能够摆出剩下的数字才可以,否则就不行。一直往下枚举。
代码如下:
//https://cn.vjudge.net/problem/UVA-12105
#include<iostream>
#include<cstdio>
#include<algorithm>
#define N 3003
const int inf=0xfffffff;
using namespace std;
int f[]={6,2,5,5,4,5,6,3,7,6};
int dp[202][N],n,m,ans,sum,mi[202],dig[202];
void init(){
mi[0]=1;
for(int i=0;i<202;i++){
for(int j=0;j<=m;j++){
dp[i][j]=inf;
}
if(i){
mi[i]=(10*mi[i-1])%m;
}
}
}
int cal(int a,int b){
return (a*mi[b])%m;
}
int cal2(int x)
{
int ans=0;
while(x){
ans+=f[x%10];
x/=10;
}
return ans;
}
//#define GLQ 1
int main()
{
#ifdef GLQ
freopen("input.txt","r",stdin);
freopen("out2.txt","w",stdout);
#endif // GLQ
int T=0;
while(scanf("%d",&n),n){
printf("Case %d: ",++T);
int high=0;
scanf("%d",&m);
init();
for(int i=0;i<10;i++){
dp[0][0]=0;
dp[1][i%m]=min(dp[1][i%m],f[i]);
if(dp[1][i%m]>n) dp[1][i%m]=inf;
}
for(int i=1;;i++){
bool flag=0;
for(int j=0;j<m;j++){
for(int x=0;x<10;x++){
dp[i+1][(j+cal(x,i))%m]=min(dp[i+1][(j+cal(x,i))%m],dp[i][j]+f[x]);
if(dp[i+1][(j+cal(x,i))%m]>n) dp[i+1][(j+cal(x,i))%m]=inf;
else flag=1;
}
}
if(dp[i][0]!=inf) high=i;
if(!flag) {
break;
}
}
int cnt=0;
int tmpn=n;
int tmpm=m ;
if(!high){
printf("-1\n");
continue;
}
for(int i=high;i>0;i--){
for(int j=9;j>=0;j--){
if(dp[i-1][(tmpm-cal(j,i-1)+m)%m]+f[j]<=tmpn){//n和m需要修改
tmpn-=f[j];
dig[cnt]=j;
tmpm=(tmpm-cal(j,i-1)+m)%m;
cnt++;
break;
}
}
}
for(int i=0;i<high;i++){
printf("%d",dig[i]);
}
printf("\n");
}
return 0;
}