题解——HDU 4734 F(x) (数位DP)

这道题还是关于数位DP的板子题

数位DP有一个显著的特征,就是求的东西大概率与输入关系不大,理论上一般都是数的构成规律

然后这题就是算一个( F(A) )的公式值,然后求( left [ 0 ,  B ight ] )区间内( F(x) )不大于( F(A) )的数的个数

所以由数据范围很容易得到计算出最大值不会超过4600

然后我们设状态( dp[10][4600][4600] )表示不同( F(A) )取值下的第( pos )个位置的值总和为 ( sumx )的 数的个数

显然会MLE

这时候可以用减法转换状态

用( dp[10][4600] )表示到了第( pos )个位置,还要凑( sumx )的值的数的个数

然后就可以发现一个现象,这个状态与( F(A) )无关的

然后就可做了

注意一个事情,就是求的是不大于( F(A) )的数的个数

所以最后( sumx ge 0 )就是合法状态了

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int dp[10][4600],a[10];
int dfs(int pos,int limit,int state){
  if(state<0)
    return 0;
  if(pos==-1){
    return state>=0;
  }
  if(!limit&&dp[pos][state]!=-1)
    return dp[pos][state];
  int mid=0,up=limit?a[pos]:9;
  for(int i=0;i<=up;i++){
    if((i<<(pos))<=state)
      mid+=dfs(pos-1,limit&&i==a[pos],state-(i<<(pos)));
  }
  if(!limit)
    dp[pos][state]=mid;
  return mid;
}
int solve(int A,int x){
  int fa=0,cona=0;
  while(A){
    fa+=((A%10)<<(cona));
    A/=10;
    cona++;
  }
  int con=0;
  memset(a,0,sizeof(a));
  while(x){
    a[con]=x%10;
    x/=10;
    con++;
  }
  return dfs(con-1,true,fa);
}
int main(){
  int T;
  memset(dp,-1,sizeof(dp) );
  scanf("%d",&T);
  for(int i=1;i<=T;i++){
    int A,B;
    scanf("%d %d",&A,&B);
    printf("Case #%d: %d
",i,solve(A,B));
  }
  return 0;
}
原文地址:https://www.cnblogs.com/dreagonm/p/9584647.html