hdu 4352 //数位dp+lis+状压

题目

题意

求[ L L L, R R R]里有多少数满足:十进制数位上的lis恰好等于 K K K

思路

首先这里lis需要模拟二分的过程。
然后把0是否出现,1是否出现…压缩成二进制状态,lis的时候就可以暴力插入某个值。
这里注意:不能每次都memset dp数组,每一个状态只能更新一次。(TLE)。
然后定义 d p [ i ] [ j ] [ k ] [ z ] : dp[i][j][k][z]: dp[i][j][k][z]剩下 i i i位时,是否有上界限制(这一维可以省略),二进制状态为 k k k,lis为 z z z时的状态。
然后记忆化搜索,只有当没有上界限制的时候,可以return某一个dp值,这样复杂度也是线性ok的。
注意前导零和函数跳出进入的pos。
好题!好题!

/*
   Author: Rshs
   Time:   2019-11-09-21.28
*/
#include<bits/stdc++.h>
using namespace std;
#define  FI    first
#define  SE    second
#define  LL    long long
#define  MP    make_pair
#define  PII   pair<int,int>
#define  SZ(a) (int)a.size()
const double pai = acos(-1);
const double eps = 1e-8;
const LL     mod = 1e9+7;
const int    MXN = 1e6+5;
LL L,R,K;
LL dp[25][2][(1<<10)][12];
vector<int>w;
int cc(int x){ //count weis
    int re=0;
    while(x) re=re+x%2,x/=2;
    return re;
}
int change(int sta,int i){
    for(int j=i;j<=9;j++){ // the first >= i
        if(sta&(1<<j)) {sta-=(1<<j),sta+=(1<<i);return sta;}
    }
    return sta+=(1<<i);
}
LL dfs(int pos,int sta1,int sta2,int zz){//pos,is limit?,binaryString,leadingzero
    if(pos==SZ(w)){
        if(cc(sta2)==K) return 1;
        else return 0;
    }
    //when it nolimit so can JiYiHuaDFS ,O(len)
    if(dp[SZ(w)-pos][sta1][sta2][K]!=-1&&sta1==1) return dp[SZ(w)-pos][sta1][sta2][K];
    LL re=0;
    for(int i=0;i<=9;i++){
        if(sta1==0&&i>w[pos]) continue;
        int tsta2=change(sta2,i);
        if(zz&&i==0) tsta2=0;//leading zero can't count
        re=re+dfs(pos+1,(( i==w[pos] )&&( sta1==0 ))^1,tsta2,(zz==1)&&(i==0));
    }
    return dp[SZ(w)-pos][sta1][sta2][K]=re;
}

LL f(LL x){
    LL tx=x;w.clear();
    while(tx) w.push_back(tx%10),tx/=10;
    if(x==0) w.push_back(0);
    //while(SZ(w)<5)w.push_back(0);
    reverse(w.begin(),w.end());

    return dfs(0,0,0,1);
}
int MAIN(int avg){
    scanf("%lld%lld%lld",&L,&R,&K);
    //cout<<f(R)<<endl;
    //cout<<f(L-1)<<endl;
    printf("Case #%d: %lld\n",avg,f(R)-f(L-1));
    return 0;
}
int main(){
    memset(dp,-1,sizeof(dp));
    int ca;cin>>ca;for(int i=1;i<=ca;i++) MAIN(i);
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值