版权声明:本文为lzr原创文章,欢迎大家转载,如需转载请注明:By CQBZ LZR https://blog.csdn.net/qq_37656398/article/details/81841213
UVa 12105 Bigger is Better
题目
◇题目传送门◆(由于UVa较慢,这里提供一份vjudge的链接)
◇题目传送门(vjudge)◆
题目大意
使用不超过
根的火柴摆出一个尽量大的、能够被
整除的正整数。数字的摆放方法如下图所示:
思路
一道极其明显的数位DP。。。
相信大家都看了紫书的两种做法(但是我根本没有看懂)。但是我在这里将给出一个极其简洁的做法。
所以我们可以定义状态 为使用不超过 根火柴,拼出来的数模 为 的数的最长长度。
因为在火柴根数相等的情况下,拼出来的数越长,数就越大。
则极易列出状态转移方程:
其中 为当前往数的末尾中添加的一位数字, 表示拼出数字 需要的火柴数量。
因为要输出方案,所以计算时记录一下路径,逆序输出即可。
是不是很简洁?:-)
正解代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int Maxn=100;
const int Maxm=3000;
const int ned[]={6,2,5,5,4,5,6,3,7,6};
int N,M;
int f[Maxn+5][Maxm+5];
int p[Maxn+5][Maxm+5];
int main() {
#ifdef LOACL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
int cas=0;
while(scanf("%d %d",&N,&M)==2) {
memset(f,-1,sizeof f);
memset(p,-1,sizeof p);
for(int i=0;i<=N;i++)
for(int j=0;j<M;j++) {
if(j==0)f[i][j]=0;
for(int d=9;d>=0;d--)//注意逆序枚举!!!
if(i>=ned[d]) {
int t=f[i-ned[d]][(j*10+d)%M];
if(t>=0&&t+1>f[i][j]) {//只有当状态合法时才转移
f[i][j]=t+1;
p[i][j]=d;
}//记录当前所选中的数字d
}
}
printf("Case %d: ",++cas);
if(p[N][0]<0) {
printf("-1");
} else {
int i=N,j=0;//逆序输出
for(int d=p[i][j];d>=0;d=p[i][j]) {
printf("%d",d);
i-=ned[d];
j=(j*10+d)%M;
}
}
puts("");
}
return 0;
}