题目HDU 4734 F(x):
题目大意是给你两个数A,B,定义F(A)= 每个数位的数 * 2 ^ (位数 - 1)。求 0 - B 区间里的 F(x) <= F(A) 的数字的个数。
一个数位DP的做法(TLE):
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[1005], FA, poww[15], dp[15][10005];
ll dfs(ll pos, ll lim, ll hav)
{
if(hav > FA)return 0;
if(pos == -1)return 1;
if(lim == 0 && dp[pos][hav] != -1)return dp[pos][hav];
ll len = lim ? a[pos] : 9, ans = 0, i;
for(i = 0; i <= len; i++)
{
if(hav + i * poww[pos] <= FA)
ans += dfs(pos - 1, lim && (i == len), hav + i * poww[pos]);
}
if(lim == 0)dp[pos][hav] = ans;
return ans;
}
ll slove(ll n, ll m)
{
FA = 0;
ll pos = 0, k = 1, ans;
while(n)
{
FA += k * (n % 10);
n /= 10;
k *= 2;
}
while(m)
{
a[pos++] = m % 10;
m /= 10;
}
ans = dfs(pos - 1, 1, 0);
}
int main()
{
ll t, n, m, i, j, k = 1, ca = 1;
for(i = 0; i <= 13; i++)poww[i] = k, k *= 2;
scanf("%lld", &t);
while(t--)
{
scanf("%lld %lld", &n, &m);
memset(dp, -1, sizeof(dp));
ll ans = slove(n, m);
printf("Case #%lld: %lld\n", ca++, ans);
}
return 0;
}
尽管加了记忆化搜索,但还是超时了,然后分析发现:
每次都要 memset(dp),用了很多时间,然后想办法优化它。
如果我们把拥有价值改变一下,变成剩余价值,即不再是它有多少,而是它还能加多少价值,这样,它的边界不再是可变的F(A),而是固定的值(0)。这样,便可以只memset(dp)一次,不需要每组都初始化了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[15];
ll dp[15][200005];
ll po[15], maxx;
ll F(ll x)
{
ll pos = 0, ans = 0, num, pi = 1;
while(x)
{
num = x % 10;
x /= 10;
ans += num * pi;
pi *= 2;
}
return ans;
}
ll dfs(ll pos, ll sta, ll lim)
{
if(sta < 0)return 0;
if(pos == -1)return 1;
if(lim == 0 && dp[pos][sta] != -1)return dp[pos][sta];
ll len = lim ? a[pos] : 9;
ll ans = 0;
for(ll i = 0; i <= len; i++)
{
ll w = po[pos] * i;
if(sta - w < 0)continue;
else
{
ans += dfs(pos - 1, sta - w, lim && i == a[pos]);
}
}
if(lim == 0)dp[pos][sta] = ans;
return ans;
}
ll slove(ll x)
{
ll pos = 0;
while(x)
{
a[pos++] = x % 10;
x /= 10;
}
ll ans = dfs(pos - 1, maxx, 1);
return ans;
}
int main()
{
ll t, k, i, j, m, n;
k = 1;
for(i = 0; i <= 15; i++)po[i] = k, k *= 2;
scanf("%lld", &t);
for(ll q = 0; q < 15; q++)
{
for(ll w = 0; w < 200005; w++)dp[q][w] = -1;
}
for(i = 1; i <= t; i++)
{
scanf("%lld %lld", &n, &m);
maxx = F(n);
//cout << maxx << endl;
ll ans = slove(m);
if(0 <= maxx)ans++;
printf("Case #%lld: %lld\n",i, ans - 1);
}
return 0;
}
总结:遇到超时问题时可以试着转换方法,反过来考虑。