数位dp,用记忆化搜索写,公认:好些且无脑!!
付经典连接
http://blog.csdn.net/cmonkey_cfj/article/details/7798809
数位DP
•问题:在给定区间[A,B]内,找满足要求的数。
•要求一般和数大小无关,而与数的组成有关
•例如,递增的,1234,2579…
• 双峰的,19280,26193…
• 含49的,49, 149, 1492…
• 整除13的,26, 39…
•麻烦在于,规模大,位数>100,不能枚举。
•并且区间往往不是整百整千,需要小心处理边界
•注意
–记忆化搜索思路清晰
–开适当空间(能省则省)
–寻找合适的状态,简化计算量
当中很多的题都会维护:能否达到上线flag,因为要保证不能超区间范围
原来是用递推来写的,现在改成记忆化搜索来写(不要62)
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
using namespace std;
int n,m,li[7];
int f[8][2];
int dfs(int j,bool state,bool limit)
{
if (j==0) return 1;
if (!limit && f[j][state]) return f[j][state];
int up=limit ? li[j]:9,ans=0;
for (int i=0;i<=up;i++)
{
if (i==4) continue;
if (state && i==2) continue;
ans+=dfs(j-1,i==6? true:false,limit && i==li[j]?true:false);
}
if (!limit) f[j][state]=ans;
return ans;
}
int work(int n)
{
memset(f,0,sizeof(f));
memset(li,0,sizeof(li));
int tot=0;
while (n)
{
li[++tot]=n%10;
n=n/10;
}
return dfs(tot,false,true);
}
int main()
{
while (scanf("%d%d",&n,&m)!=EOF)
{
if (n==0&&m==0) break;
printf("%d\n",work(m)-work(n-1));
}
return 0;
}
数位dp的思路,以位为方式dp(hdu2089)
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int f[12][10],n,m;
int work(int n)
{
int id[12];
memset(id,0,sizeof(id));
while (n)
{
id[++id[0]]=n%10;
n=n/10;
}
int ans=0;
for (int i=id[0];i>=1;i--)
{
for (int j=0;j<id[i];j++)
if (j!=4&&!(id[i+1]==6&&j==2)) ans+=f[i][j];
if (id[i]==4||(id[i]==2&&id[i+1]==6)) break;
}
return ans;
}
int main()
{
f[0][0]=1;
for (int i=1;i<=7;i++)
for (int j=0;j<=9;j++)
for (int l=0;l<=9;l++)
if (j!=4&&!(j==6&&l==2))
f[i][j]+=f[i-1][l];
while (scanf("%d%d",&n,&m)!=EOF)
{
if (n==0&&m==0) break;
if (n>m)swap(n,m);
printf("%d\n",work(m+1)-work(n));
}
return 0;
}