链接
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3257
题解
水平低啊
先无脑设计一个
DP
D
P
显然位数越多的越大,
肯定有人问:那前导
0
0
要不要考虑?
不用考虑,因为你把签到都放到后面去也可以构造一个答案,比如
0123
0123
如果可行,那么
1230
1230
肯定也可行
所以我就是先保证位数最多,然后考虑怎样填满这些位使得数字尽量大
先无脑搞一个状态
f[i][j][k]
f
[
i
]
[
j
]
[
k
]
表示用恰好
i
i
根火柴摆成的
k
k
位数字的方案存不存在
那么无脑转移就行了,时间复杂度太大,考虑优化
显然我的状态的值是一个布尔值,表示的是方案存不存在,这样就很浪费,我们把一维拿到右边去,表示
modM=j
mod
M
=
j
的
k
k
位数摆出来最少用多少火柴
然后转移:,其中
x
x
是我枚举的,瞎
dp
d
p
一下就求出来了
到这里都很好想,我现在已经知道了这个数字有多少位,我要构造一个字典序最大的,咋整?
其实想到了也不难,反正我要让字典序最大嘛,就直接从贪心地角度从高位到地位一位一位地填,从
x=9
x
=
9
枚举到
x=0
x
=
0
,然后看下
s+c[x]+f[(S+j)modM][k−1]
s
+
c
[
x
]
+
f
[
(
S
+
j
)
mod
M
]
[
k
−
1
]
是不是不大于
N
N
,如果不大于,就在这里填
x
x
,是我已经填的数字消耗的火柴总和,
S
S
是实际组成的数字对取模的值
代码
//DP
#include <bits/stdc++.h>
#define maxm 3010
#define maxk 60
#define inf 0x3f3f3f3f
#define mod(x) ( ( (x)%M+M )%M )
using namespace std;
int f[maxm][maxk], d[maxm][maxk], N, M, K, c[15]={6,2,5,5,4,5,6,3,7,6}, mi[100], cnt, len;
void init()
{
int i, j, k;
memset(f,inf,sizeof(f));
memset(d,0,sizeof(d));
mi[1]=1;
for(i=2;i<=50;i++)mi[i]=mi[i-1]*10%M;
}
void dp()
{
int i, j, x, t;
f[0][0]=0;
for(j=0;j<=50;j++)for(i=0;i<M;i++)
{
for(x=0;x<=9;x++)
if(f[i][j]+c[x]<=N)
{
t=(i+x*mi[j+1])%M;
if(f[t][j+1]==f[i][j]+c[x])d[t][j+1]=max(d[t][j+1],x);
else if(f[t][j+1]>f[i][j]+c[x])f[t][j+1]=f[i][j]+c[x], d[t][j+1]=x;
}
}
for(len=50;len and f[0][len]==inf;len--);
}
void show()
{
if(len==0)
{
printf("-1\n");
return;
}
int i, j, s=0, m=0, to;
for(i=len;i;i--)
{
for(j=9;j>=0;j--)
{
to=mod(m-j*mi[i]);
if(s+c[j]+f[to][i-1]<=N)
{
putchar(j+48);
m=to;
s+=c[j];
break;
}
}
}
putchar('\n');
}
int main()
{
int i, j, k, x, kase=0, t, a;
string ans, s;
while(scanf("%d",&N),N)
{
printf("Case %d: ",++kase);
scanf("%d",&M);
init();
dp();
show();
}
return 0;
}