題意:給你A和B,讓你求0~~B中F(x)< = F(A) 的x的個數,滿足F(x) = An * 2n-1 + An-1 * 2n-2 + ... + A2 * 2 + A1 * 1;
分析:當我做到這題的時候一個很清晰的細節就是,給我們10的9次方範圍,0.5s的時限,最大的F(x)總和也不到7000,T(測試樣例)還很大,容易想到的就是數位DP,至於怎麼數位DP,可以發現,它的第i位和第i+1位有關聯,於是得到狀態轉移方程:dp [ i ] [ j ] [ k + 2 ^ ( i - 1 ) * j ] + = sum { dp[ i - 1 ] [ t ] [ k ] } ; dp [ i ] [ j ] [ k ]表示第i位是J的時候總和爲K的個數;
代碼如下:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
const int maxn = 7000;
int dp[12][12][maxn];
int sum[12][12][maxn];
void init(){
memset(dp,0,sizeof(dp));
memset(sum,0,sizeof(sum));
dp[0][0][0]=1;
for(int i=1;i<11;i++){
for(int j=0;j<=9;j++){
for(int k=0;k<maxn-500;k++){
int t1=k+(1<<(i-1))*j;
for(int t=0;t<=9;t++){
dp[i][j][t1]+=dp[i-1][t][k];
}
if(k==0) sum[i][j][k]=dp[i][j][k];
else
sum[i][j][k]=sum[i][j][k-1]+dp[i][j][k];
}
}
}
}
int w[12];
int dige(int n){
int ret=0;
while(n){
w[++ret]=n%10;
n/=10;
}
return ret;
}
int work(int n,int M){
int dig=dige(n);
// printf("%d\n",dig);
int flag=0;
int ans=0;
for(int i=dig;i>=1;i--){
for(int j=0;j<w[i];j++){
ans+=sum[i][j][M-flag];
}
flag+=w[i]*(1<<(i-1));
if(flag>M) break;
}
return ans;
}
int main(){
int T;
init();
scanf("%d",&T);
int con=1;
while(T--){
int a,b;
scanf("%d %d",&a,&b);
int fa=0;
int wei=0;
while(a){
fa+=(a%10)*(1<<wei);
a/=10;
wei++;
}
printf("Case #%d: %d\n",con++,work(b+1,fa));
}
}