不要62 http://acm.hdu.edu.cn/showproblem.php?pid=2089
求 (n,m)之间不含 4 和 62 的个数 , (0<n≤m<1000000),当然可以暴力打表过。 数位dp
DP[i][j]表示 从高位起 的 当前第i位状态为j的 且不在数值边界的 符合条件的 数字个数。上一位 是6 j=1,否则j=0;
记忆化搜索,注意数值边界不能用dp[][]记忆化。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<cmath>
#include<map>
#include<set>
using namespace std;
#define INF 1000000000
#define N 10
int dp[N][4],len;
char lim[N];
void tostring(int x)
{
len=0;
while(x) lim[++len]=x%10,x/=10;
}
int dfs(int i,int j,int flag)//i,j表示dp[i][j]中的意思,flag表示上位是否为边界
{
if(i==1) return 1;
if(!flag && dp[i][j]!=-1) return dp[i][j];//记忆化,边界没有记忆化
int bound=flag? lim[i-1]:9;//确定此位的数字上限
int ret=0;
for(int k=0;k<=bound;k++)
{
if(k==4) continue;
if(j==1 && k==2) continue;
ret+=dfs(i-1,k==6? 1:0,flag && k==bound);
}
return flag? ret:dp[i][j]=ret;//不记忆化 边界
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m),n||m)
{
n--;
tostring(m);
memset(dp,-1,sizeof(dp));
int tmp1=dfs(len+1,0,1);
tostring(n);
memset(dp,-1,sizeof(dp));
int tmp2=dfs(len+1,0,1);
printf("%d\n",tmp1-tmp2);
}
return 0;
}
Bomb
http://acm.hdu.edu.cn/showproblem.php?pid=3555
求(1,N) 中 含有49的数字的个数, (1 <= N <= 2^63-1)。
dp[i][j]表示 当前为第 i 位,状态为j的在范围内 不是边界上 符合条件的 数字的个数,j=1表示上一位出现4,j=2表示第1到i-1位出现49,否则为j=0。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<cmath>
#include<map>
#include<set>
using namespace std;
#define INF 1000000000
#define N 25
typedef __int64 LL;
LL dp[N][4];// 0 none 1-->4 2-->49
LL t,n,len;
char lim[N];
void tostring()
{
len=0;
while(n) lim[++len]=n%10,n/=10;
}
LL dfs(int i,int j,int flag)
{
if(i==1) return j==2;
if(!flag && dp[i][j]!=-1) return dp[i][j];
int bound=flag? lim[i-1]:9;
LL ret=0;
for(int k=0;k<=bound;k++)
{
if(j==2) ret+=dfs(i-1,2,flag&& k==bound);
else if(k==9 && j==1) ret+=dfs(i-1,2,flag && k==bound);
else if(k==4) ret+=dfs(i-1,1,flag && k==bound);
else ret+=dfs(i-1,0,flag && k==bound);
}
return flag? ret:dp[i][j]=ret;
}
int main()
{
scanf("%I64d",&t);
for(LL i=1;i<=t;i++)
{
scanf("%I64d",&n);
tostring();
memset(dp,-1,sizeof(dp));
printf("%I64d\n",dfs(len+1,0,1));
}
return 0;
}
Good Numbers http://acm.hdu.edu.cn/showproblem.php?pid=4722
求(A,B)之间 ,各位上数字之和 为10 的 数字的个数 (0 <= A <= B <= 1018).
dp[i][j] 表示 第i位到末位的数字之和的MOD10==j的数字的个数,依旧是边界不能记忆化。 递归到最低位是只有j余数==0才return 1;
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<cmath>
#include<map>
#include<set>
using namespace std;
#define INF 1000000000
#define N 22
typedef __int64 LL;
LL t,len;
LL dp[N][12];
char lim[N];
void tostring(LL x)
{
len=0;
while(x) lim[++len]=x%10,x/=10;
}
LL dfs(int i,int j,int flag)
{
if(i==1) return !j;
if(!flag && dp[i][j]!=-1) return dp[i][j];
int bound=flag? lim[i-1]:9;
LL ret=0;
for(int k=0;k<=bound;k++)
{
ret+=dfs(i-1,(10+j-k)%10,flag && k==bound);
}
return flag? ret:dp[i][j]=ret;
}
int main()
{
scanf("%I64d",&t);
LL a,b;
for(LL i=1;i<=t;i++)
{
scanf("%I64d%I64d",&a,&b); a--;
tostring(a);
memset(dp,-1,sizeof(dp));
LL tmp1=dfs(len+1,0,1);
tostring(b);
memset(dp,-1,sizeof(dp));
LL tmp2=dfs(len+1,0,1);
printf("Case #%I64d: %I64d\n",i,tmp2-tmp1);
}
return 0;
}
windy数 UESTC 1307
不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。
windy想知道,在A和B之间,包括A和B,总共有多少个windy数? 1 <= A <= B <= 2000000000 。
dp[i][j]表示 当前为第i位 j>=0表示当前数字为j 满足条件的 数字的个数,j<0表示 第i位之前全是前导0,第j位可以为范围内的任意数,直到 某位不为0,j的状态改变。详见代码。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<cmath>
#include<map>
#include<set>
using namespace std;
#define INF 1000000000
typedef long long LL;
#define N 15
int dp[N][14],n,len;
char lim[N];
void tostring(int x)
{
len=0;
while(x) lim[++len]=x%10,x/=10;
}
int dfs(int i,int j,int flag)
{
if(i==1) return 1;
if(!flag && dp[i][j]!=-1) return dp[i][j];
int bound=flag? lim[i-1]:9;
int ret=0;
for(int k=0;k<=bound;k++)
{
if(j<0 && k==0) ret+=dfs(i-1,-1,flag && k==bound);
else if(j<0) ret+=dfs(i-1,k,flag && k==bound);
else
{
if(abs(k-j)<2) continue;
ret+=dfs(i-1,k,flag && k==bound);
}
}
return flag? ret:dp[i][j]=ret;
}
int main()
{
int a,b;
while(~scanf("%d%d",&a,&b))
{
a--;
tostring(b);
memset(dp,-1,sizeof(dp));
int tmp1=dfs(len+1,-1,1);
tostring(a);
memset(dp,-1,sizeof(dp));
int tmp2=dfs(len+1,-1,1);
printf("%d\n",tmp1-tmp2);
}
return 0;
}
B. Little Elephant and Elections http://codeforces.com/problemset/problem/258/B
4和7是幸运数字。一共7个party,给这7个分别一个不同数字x,范围 (7 ≤ m ≤ 109),让一个特殊的party得到的数字中含有的幸运数字的个数,比其他6个party所有的数字中含有幸运数字的个数的总和还要多。 求多少种分配方案,使得满足条件。
首先 ,数位DP处理出来1~m之间 含有i个幸运数的数字的个数num[i] ,0<=i<=9;
dp[i][j] 表示到第i位 且此位之前 含有j个幸运数 且满足最终 含有k个幸运数的范围内的 数字的个数。
num[i]表示m的范围内 含有i个幸运数的数字的个数。
之后枚举特殊的party的 选取的幸运数字个数, DFS 暴力求出方案数.
加入6个party中有r个选取了含有q个幸运数的数字此时的方案数=num[特殊party的选取]*(num[q]*(num[q]-1)......(num[q]-r+1))
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef __int64 LL;
#define INF 1000000000
#define N 15
#define MOD 1000000007
LL m,dp[N][N],num[N],len,sum=0,vis[N],par[N],vv[N],tmp;
char lim[N];
void tostring()
{
len=0;
while(m) lim[++len]=m%10,m/=10;
}
LL dfs(LL i,LL j,LL flag,LL k)
{
if(j>k) return 0;
if(i==0) return j==k;
if(!flag && dp[i][j]!=-1) return dp[i][j];
LL bound=flag? lim[i]:9;
LL ret=0;
for(LL p=0;p<=bound;p++)
{
if(p==4 ||p==7) ret+=dfs(i-1,j+1,flag && p==bound,k);
else ret+=dfs(i-1,j,flag && p==bound,k);
}
return flag? ret:dp[i][j]=ret;
}
void dfs2(LL i,LL j)
{
if(i>=7)
{
memset(vv,0,sizeof(vv));
LL tt=1;
for(LL k=1;k<=6;k++)
{
tt*=(num[par[k]]-vv[par[k]]);
tt%=MOD;
vv[par[k]]++;
}
tmp+=tt;
return ;
}
for(LL k=0;k<=j;k++)
{
if(vis[k]>=num[k]) continue;
vis[k]++;
par[i]=k;
dfs2(i+1,j-k);
vis[k]--;
}
}
int main()
{
scanf("%I64d",&m);
tostring();
for(LL i=0;i<=9;i++)
{
memset(dp,-1,sizeof(dp));
num[i]=dfs(len,0,1,i);
}
num[0]--;
for(LL i=1;i<=9;i++)
{
if(!num[i]) continue;
memset(vis,0,sizeof(vis));
tmp=0;
dfs2(1,i-1);
tmp*=num[i];
sum+=tmp;
sum%=MOD;
}
printf("%I64d\n",sum);
return 0;
}