一直想做这样的题,果然做完之后有了些领悟,代码打得无比生疏,花了老长时间才过的~
刚开始也没神马思路,搜了搜解题报告,大多数都是爆搜,这样没有意思,后来找到了一篇用记忆化优化搜索做的,给力~
dp[i][k][j]:是否存在一个n位数,i位,以k数字大头,模n为j.值分0,1,2:若为0,不存在这样的数,值为1,上一位也就是第二位为0,值为2,上一位为1.
way[i][k][j]:状态与前一个对应,记录的是上一位的余数,前提是dp[i][k][j]>0
这样讲,如果我们用爆搜做此题,函数应该是dfs(i,k,j),那么dp[i][k][j]就是函数值,通过函数值可以推出上一次函数的k,而way[i][k][j]就是上一次函数的j。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#define N 105
#define ss(a) scanf("%d",&a)
#define cl(a) memset(a,0,sizeof(a))
using namespace std;
int dp[N][3][2*N],way[N][3][2*N],arr[N];
int main()
{
int i,j,n;
while (ss(n)!=EOF)
{
if (n==0) break;
arr[1]=1;
for (i=2;i<=100;i++) arr[i]=(arr[i-1]*10)%n;
cl(dp);cl(way);
dp[0][0][0]=1;
for (i=1;i<=100;i++)
{
for (j=0;j<n;j++)
if (dp[i-1][0][j])
{
dp[i][0][j]=1;
way[i][0][j]=j;
}
else if (dp[i-1][1][j])
{
dp[i][0][j]=2;
way[i][0][j]=j;
}
for (j=0;j<n;j++)
{
int t=(j+arr[i])%n;
if (dp[i-1][0][j])
{
dp[i][1][t]=1;
way[i][1][t]=j;
}
else if (dp[i-1][1][j])
{
dp[i][1][t]=2;
way[i][1][t]=j;
}
}
if (dp[i][1][0]) break;
}
int a=1,b=0,p,q;
for (j=i;j>=1;j--)
{
cout<<a;
p=a;q=b;
b=way[j][p][q];
a=dp[j][p][q]-1;
}
cout<<endl;
}
return 0;
}