首页 > 代码库 > hdu5106 数位dp

hdu5106 数位dp

这题说的是给了一个二进制数R , 计算出 在[0,R) 区间内的数, 二进制中有n个1 个和

n<=1000; R<2^1000, 这样 用dp[len][lee] 表示在第len位的时候已经放了lee个1 个总和, num[len][lee] 表示在第len位的时候已经放了lee个1 个数有多少个,这样只要考虑当前位置放1与不放1 记忆化进行搜索

#include <iostream>#include <algorithm>#include <cstdio>#include <string.h>using namespace std;const int maxn =1005;typedef long long ll;const ll mod=1000000007;ll dp[maxn][maxn];ll num[maxn][maxn];char str[maxn];ll dig[maxn];void dfs(int loc, int lee , bool e, ll &value, ll &ge){       value = ge = 0;       if(e==false&& num[loc][lee]!=-1 ){           value=dp[loc][lee]; ge =num[loc][lee]; return ;       }       if(loc==0&&lee==0){          value=0, ge=1; return ;       }       int we = e? str[loc-1]-0:1;       ll ans=0, nu=0;       if(we==1){           if(loc-1>=lee){               dfs(loc-1,lee,false,ans,nu);               value=ans, ge=nu;           }           if( lee > 0 ){               dfs(loc-1,lee-1,e,ans, nu);               value=( value + ans + ( dig[loc-1] )*nu%mod  )%mod;               ge = ( nu + ge )%mod;           }       }else{           if(loc-1>=lee){            dfs(loc-1,lee,e,ans,nu);            value= ans; ge=nu;           }       }    if(e==false) { dp[loc][lee] = value, num[loc][lee]=ge;  }}int main(){    int n;    dig[0]=1;    for(int i=1; i<=1000; ++i)        dig[i]=(dig[i-1]*2)%mod;    while(scanf("%d%s",&n,str)==2){        int len = strlen(str);        for(int i=0; i<len/2; ++i){              char c = str[i];              str[i]=str[len-1-i];              str[len-1-i]=c;        }        memset(dp,0,sizeof(dp));        memset(num,-1, sizeof(num));        memset(num[0],0,sizeof(num[0]));        num[0][0]=1;        ll ans=0,nu=0;        dfs( len , n , true, ans, nu);         ll digt=0;nu=0;        for(int i=0; i<len; ++i){             if(str[i]==1){                 digt++; nu= ( nu + dig[i])%mod;             }        }             if(digt==n) ans=(ans-nu+mod)%mod;        printf("%I64d\n",ans);    }    return 0;}
View Code

 

hdu5106 数位dp