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

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

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

然后这题就是算一个\( F(A) \)的公式值,然后求\( \left [ 0 ,  B \right ] \)区间内\( 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\n",i,solve(A,B));
  }
  return 0;
}

猜你喜欢

转载自www.cnblogs.com/dreagonm/p/9584647.html