HDU 4734 F(x) ★(数位DP)

题意

一个整数 (AnAn-1An-2 ... A2A1), 定义 F(x) = An * 2n-1 + An-1 * 2n-2 + ... + A2 * 2 + A1 * 1,求[0..B]内有多少数使得F(x) <= F(A)。多组数据,T <= 10000

思路

成都网赛……都是泪T_T……

很裸的数位DP……一开始我的dp状态是dp[pos][fx],fx表示当前枚举到fx为多少,判断fx<=fa。但这样设计状态的一个问题是对于不同的A,dp[][]表示的状态不同,所以每个T都有memset dp数组,并且重新计算,然后就超时了哭……

后来被nocLty指点……dp状态设计成dp[pos][remain],remain表示还剩多少fA可以分配,判断remain>=0。这样对于不同的A便不影响他的状态表示。所以不用每次T都重新计算dp[][],大大缩短了时间。

总结一下,在设计数位DP状态时最好把每次询问的变量作为初始值而不是状态转移、判定时依赖的量(比如第一个状态判定条件是fx<=fa,就依赖于当前fa,这样就不好),这样才可以最大限度的发挥记忆化的作用。

代码

[cpp]
#include &lt;iostream&gt;
#include &lt;cstdio&gt;
#include &lt;cmath&gt;
#include &lt;algorithm&gt;
#include &lt;string&gt;
#include &lt;cstring&gt;
#include &lt;vector&gt;
#include &lt;set&gt;
#include &lt;stack&gt;
#include &lt;queue&gt;
#define MID(x,y) ((x+y)/2)
#define MEM(a,b) memset(a,b,sizeof(a))
#define REP(i, begin, end) for (int i = begin; i &lt;= end; i ++)
using namespace std;
vector &lt;int&gt; num;
int dp[11][10000];
int dfs(int pos, int remain, int limit){
if (pos == -1){
return (remain &gt;= 5000);
}
if (!limit &amp;&amp; ~dp[pos][remain]) return dp[pos][remain];
int end = limit?num[pos]:9;
int res = 0;
for (int i = 0; i &lt;= end; i ++){
res += dfs(pos-1, remain-(1&lt;&lt;pos)*i, limit &amp;&amp; (i == end));
}
if (!limit){
dp[pos][remain] = res;
}
return res;
}
int main(){
int t;
scanf(&quot;%d&quot;, &amp;t);
MEM(dp, -1);
for (int ca = 1; ca &lt;= t; ca ++){
int A, B;
scanf(&quot;%d %d&quot;, &amp;A, &amp;B);
num.clear();
while(B){
num.push_back(B % 10);
B /= 10;
}
int fa = 0;
for (int i = 0; A; A /= 10, ++ i){
fa += (1 &lt;&lt; i) * (A % 10);
}
printf(&quot;Case #%d: %d\n&quot;, ca, dfs(num.size()-1, 5000+fa, 1));
}
return 0;
}
[/cpp]

上一篇:HDU 4734 - F(x) - [数位DP][memset优化]


下一篇:JAVA回调函数ANDROID中典型的回调地方