数位DP
讲一下具体思路,自己是看了kuangbin的板子来做的
DP的关键在于如何去寻找转移方程,本道题用到了一种相减的思路
A,B分别代表题目描述的变量
DP[pos][sum] pos 不用多说就是数位, sum这里不是指一个数的和,这里的sum指的是距离凑满F(A)还需要多少数这样子转移方程就可以记忆化了,为什么呢?当第一步的时候指的是DP[pos][F(A)],但是继续往下dfs,因为每一步都要减去一个数,这个时候sum,更新成了一个新的数字,这个时候我们求得就是距离凑满新的sum还需要多少的数,这样就跟F(A)相当于脱离了,每一个求出来的DP[][]是可以作用于所有的数的如果还不明白可以自己在程序上做几个测试点帮助自己理解,只要记住DP[pos][sum]求的是在pos位,距离凑满sum的数有多少就行
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int case_ = 0;
int dp[20][6000];
int p[20];
int dfs(int pos, int sum, bool limit){
if(sum < 0) return 0;
if(pos == -1 && sum >= 0) return 1;
if(!limit && dp[pos][sum] != -1) return dp[pos][sum];
int up = limit? p[pos]: 9;
int ans = 0;
for(int i = 0; i <= up; i++){
ans += dfs(pos-1, sum - i*(1<<pos), limit && i==p[pos]);
}
if(!limit) dp[pos][sum] = ans;
return ans;
}
int F(int x){
int sum = 0;
int len = 0;
while(x){
sum += (x%10)*(1<<len);
len++;
x /= 10;
}
return sum;
}
int solve(int y, int x){
int len = 0;
while(x){
p[len++] = x%10;
x /= 10;
}
return dfs(len-1, F(y), true);
}
int main()
{
int TestCase;
memset(dp, -1, sizeof(dp));
scanf("%d", &TestCase);
int A, B;
while(TestCase--){
scanf("%d%d", &A, &B);
printf("Case #%d: %d\n", ++case_, solve(A, B));
}
return 0;
}