HDU 4734 F(x)
题目链接:HDU 4734 F(x)
题目大意:
定义十进制数\(x\)的权值为\(F(x)=A_n\times 2^{n-1}+A_{n-1}\times 2^{n-2}+...+A_2\times 2+A_1\times 1\),\(A(i)\)表示十进制数\(x\)中第\(i\)位的数字。
题目给出\(A\)、\(B\),求出\([0,B]\)有多少个权值不大于\(F(A)\)的数。
题解:
用\(dp[pos][sum]\)表示长度为\(pos\)且权值不大于\(sum\)的数的个数。
用记忆化搜索,到最低位若权值仍小于\(F(A)\)则表示该数符合。
#include <cstring>
#include <iostream>
using namespace std;
#define ll long long
int digit[20];
ll dp[20][5000], n, m;
int dfs(int pos, int sum, bool limit) {
if (pos <= 0) return sum >= 0;
if (sum < 0) return 0; // 已经比F(A)小了就不用往下搜索了
if (!limit && dp[pos][sum] != -1) return dp[pos][sum]; // 没限制则用记忆化搜索
int ans = 0;
int up = limit ? digit[pos] : 9;
for (int i = 0; i <= up; ++i) {
ans += dfs(pos - 1, sum - i * (1 << (pos - 1)), limit && i == up);
}
if (!limit) dp[pos][sum] = ans;
return ans;
}
ll F(ll x) { // F(A)
ll ans = 0, cnt = 1;
while (x) {
ans += x % 10 * cnt;
cnt *= 2;
x /= 10;
}
return ans;
}
int cal(ll x) {
int cnt = 0;
while (x) {
digit[++cnt] = x % 10;
x /= 10;
}
return cnt;
}
int main() {
int T;
cin >> T;
memset(dp, -1, sizeof(dp));
for (int Case = 1; Case <= T; ++Case) {
cin >> n >> m;
cout << "Case #" << Case << ": " << dfs(cal(m), F(n), true) << endl;
}
return 0;
}