题文:https://vjudge.net/problem/12364(或者见紫书)
题解:
因为题目中有两个限制条件,那么我们就顺着题目的意思来dp,设dp[i][j]表示目前还剩下的i个火柴,用这i根火柴所能凑出的%m是j的最大的数,那么转移就是枚举最左边的数x,那么就转移到了dp[i-shu[x]][(j*10+x)%m]。但如果用dp数组直接存数的话数组那么就要写高精度了,应为最大会有55位。
考虑设dp[i][j]存的是用这i根火柴所能凑出的%m是j的最大的数的数位,p[i][j]表示这个数首位是什么,那么dp[i][j]=max(dp[i-need[shu]][(j*10+shu)%m])+1;为了保证我们选的数是最大的,我们枚举数的时候要从大的开始枚举。最后重新检查一下最优的结构就可以把数打印出来了。
有一些细节,可以看一下代码。
代码:
#include<iostream> #include<algorithm> #include<string> #include<cstdlib> #include<cstdio> #include<cstring> #include<cmath> using namespace std; const int MAXNN=100+5; const int MAXNM=3000+5; int need[10]={6,2,5,5,4,5,6,3,7,6}; int dp[MAXNN][MAXNM],p[MAXNN][MAXNM]; int n,m; int main(){ int hhh=0; while(++hhh){ scanf("%d",&n); if(!n) break; printf("Case %d: ",hhh); scanf("%d",&m); memset(p,-1,sizeof(p)); for(int i=0;i<=n;i++) for(int j=0;j<m;j++){ int ans=-1; if(!j) ans=0; for(int shu=9;shu>=0;shu--) { if(i>=need[shu]) { int t=dp[i-need[shu]][(j*10+shu)%m]; if(t>=0&&t+1>ans) { ans=t+1; p[i][j]=shu; } } } dp[i][j]=ans; } if(p[n][0]==-1) printf("-1"); else{ int j=0; for(int now=p[n][j];now>=0;now=p[n][j]){ printf("%d",now); n-=need[now]; j=(now+j*10)%m; } } printf(" "); } }