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
",ans);
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/Opaser/p/4114726.html