让我们来写数位DP吧~

来源:互联网 发布:政务数据开放流程 编辑:程序博客网 时间:2024/05/24 01:42

HDU 2089 不要62

限制:
62 不能连号, 不能出现4

思路:
1. 对于当前位数字是 4 有限制,可以直接处理!
2.
pre_num(前一个数字) 有限制;
if(pre_num == 6) dp当前位不能取 2 状态变化.
else dp数组可以任意取.
所以对于当前位 的 前面位有两种情况,
但是我们是写的DFS,所以先对于后面的状态先更新,所以dp更新的时候对于后一位要记录dp[][0/1] (0/1代表 pre_num(=6 / !=6 ))

int dp[10][2], a[10];int DFS(int pos, int sta, bool limit){    if(pos < 0) return 1;    if(!limit && dp[pos][sta] != -1) return dp[pos][sta];    int up = limit ? a[pos] : 9;    int temp = 0;    for(int i=0;i<=up;i++){        if(i == 4) continue;        if(sta && i == 2) continue;        temp = temp + DFS(pos-1, i==6, limit && (i == up));    }    if(!limit) dp[pos][sta] = temp;    return temp;}int solve(int n){    int num = 0;    while(n){        a[num++] = n % 10;        n /= 10;    }    return DFS(num-1, 0, true);}int main(){    int n, m;    memset(dp, -1, sizeof(dp));    while(scanf("%d%d", &n, &m)){        if(!n && !m) break;        printf("%d\n", solve(m) - solve(n-1));    }    return 0;}

HDU 4734 F(x);

在区间[0, B]内有多少个 F(x) <= F(A).

F(x) = An * 2n-1 + An-1 * 2n-2 + … + A2 * 2 + A1 * 1.

限制:
1. 先考虑F(x) 的最大值,最高(2^9 - 1) * 9 = 525 * 9

数位DP(大)都是从高位搜下去的,如果按照题意考虑我们持续从最高位搞个中间值加起来!
dp可以记录,当前位是多大的 个数。
但是如何转移呢???对于之前那位,OK,我已经知道之前的和 pre_sum 是多少,
现在我们只要算一下当前pos 满足 F(A) - pre_sum >= 0的那些 DP 值.
考虑减法;
如果对于当前值就是F(A),那么之后的权值和是不是<=F(A)就行了,
是不是变成了当前值是temp,那么之后的权值和是不是<=temp就行了,
那我直接把temp直接记录,就是对于当前位 <= temp 的数量.

注意:
存数位的数组一些位置无意义但被使用,要初始化!

int dp[11][5000];int a[11];int n, m;int DFS(int pos, int sta, bool limit){    if(sta < 0) return 0;    if(pos < 0) return 1;    if(!limit && dp[pos][sta] != -1) return dp[pos][sta];    int up = limit ? a[pos] : 9;    int ans = 0;    for(int i=0;i<=up;i++)        ans = ans + DFS(pos-1, sta-(1<<pos)*i, limit && (i == up));    if(!limit) dp[pos][sta] = ans;    return ans;}int solve(){    int num = 0;    int temp = 0;    while(n){        temp = temp + (n % 10) * (1<<num);        n /= 10;        num++;    }    memset(a, 0, sizeof(a));    num = 0;    while(m)    {        a[num++] = m % 10;        m /= 10;    }    return DFS(num-1, temp, true);}int main(){    int T, cas=1;    scanf("%d", &T);    memset(dp, -1, sizeof(dp));    while(T--){        scanf("%d%d", &n, &m);        printf("Case #%d: %d\n", cas++, solve());    }    return 0;}

51nod1009 数字1的数量

1-N的数的1的数量;

数位上的思考:
1. 个位上1的数量,不考虑个位限制,也就是意味着区间[1, 9],num0 = 1;
2. 加一个十位 1的数量,不考虑个/十位限制,也就是意味着区间[1, 99],
num1 = (两位数, 1 * 10(十位为1,个位随意[0, 9]) + 1 * 9(个位为1, 十位(1, 9))) + 1(个位数, num0 = 1)
3. 再加一个百位 1的数量,不考虑个/十/百位限制,也就是意味着区间[1, 999]
num2 = (三位数, 1 * 100(百位为1,其余随意[0, 99] + 1 * 90(十位为1, 个位[0, 9], 百位[1, 9]))) + (两位数, num1)
4. 现在直接改称 <= 4 位的一个数[1, 9999],
num4 = 1 * 1000 + 1 * 9 * 100 + 1 * 9 * 100 + 1 * 9 * 100 + num2 = 1*1000 + 3*900 + num2.

显然这还是不够的,
我们还要考虑,
like 区间[1, 36], 显然[1, 9],我们现在要知道的是[10, 36], 但是3>=1, 还是没有限制
like 区间[1, 106], 其实显然[1, 99]我们都知道了, 我们现在要知道的是[100, 106], 百位==1, 总共有6个, 十位只能是0, 个位1的时候,只能是百位为1十位是0
like 区间[1, 236], 其实显然[1, 99]我们都知道了, 我们现在要知道的是[100, 236], 百位==1, 总共有37[0, 36], 个位为1,百位可以 2 1/2, 个位可以[0, 9]
like 区间[1, 216], 其实显然[1, 99]我们都知道了, 我们现在要知道的是[100, 216], 百位==1, 总共有17[0, 16], 十位为1,百位为1, 个位可以10[0, 9], 但是百位为1, 个位只能是7[0, 6]
like 区间[1, 2345], 其实显然[1, 999]我们都知道了, 我们现在要知道[1000, 2345], 千位==1, 总共有346[0, 345],
百位为 1, 千位可以1/2, 后面就是 46 [0, 45]
总结一下???
对于这里我们可以考虑特殊情况然后暴力可以得到。
感觉方法太烂!!!
考虑递推,我们从低到高处理,
答案是累加的过程。
like 区间[1, 36]至36, 对于最高位是1, 都OK就是加[0, 9] 然后考虑尾数1的个数(统筹考虑), 那么就是 +=num1*3([0,1,2]), 之前的就是高位为3的情况
like 区间[1, 106]至106, 对于最高位是1, 那么就是 +=7[0, 06](+尾数+1), 然后加上num2*1(最高位取0)
like 区间[1, 236]至236, 对于最高位是1, 都OK就是加[0, 99], 然后考虑尾数1的个数, 那么就是 +=num2*3([0,1,2]) + 之前的就是高位为3的情况
OK!

也可以考虑DFS,考虑每一位的数的贡献。


CodeForces55D Beautiful numbers

题意:
查询区间有多少个 整除 自己每位上非0的数

限制:
0无限制、能整除每一位。
X % Y == 0
X * 10 % Y == 0

方案1.[GG]
一开始开了临时值然后直接dp[pos]巨撒比,明明这一位对于前一位是有特殊关系的,然后直接后一位求出来了,对于前一位的另一个都不满足了
like -> if(!limit && dp[pos] != -1) return dp[pos];
死于–>转移

方案2.[GG]
想了开状态来记录 [至当前] 能 (%x == 0) 的方案数,但是没用啊!!!
但是中间写崩了————对于中间的时候,很撒比地 如果 [(中间值*10 + i % i) != 0] 直接不管了!!
woc??? 你怎么知道的??? 对于中间的时候 虽然现在不能模i等于0,不代表之后加一些数可以实现膜i等于0吧
死于—>转移

总结一下————
1.显然我们是需要中间值的,用来转移
等等!这个中间好大啊!怎么优化一下,%(123456789的LCM) 就好了嘛.
2.我们无法在中间判断这个值是不是不合法!!!NO WAY!!!所以我们还要记录到个位的时候是不是对所有的位都能膜他等于0
Sooooooooo!!!我们是不是要记录前面这些位,怎么记录————乘起来很棒棒!!!————LCM更加棒棒!!!
直接存 空间复杂度好像很大!!!我们考虑LCM有多少个?(话说怎么求啊)这些LCM (1*2*3*…*8*9) 的约数嘛, 我们可以离散化一下

方案3.
现在我们数位DP, 每次会记录一个中间值temp,一个lcm, 不满足的话 就是到了最低位 temp % lcm != 0;
那么我们开 dp[][][]三维就可以刚了!

int mod, hs[3000], xs[55];void init(){    mod = 2520;    int num = 0;    for(int i=1;i<=mod;i++)        if(mod % i == 0){            xs[++num] = i;            hs[i] = num;        }//    printf("%d\n", num);}int a[25];LL dp[25][3000][55];int LCM(int x, int y){    return x * y / __gcd(x, y);}LL DFS(int pos, int temp, int lcm, bool limit){    if(pos < 0){        if(temp % lcm == 0) return 1;        return 0;    }    if(!limit && dp[pos][temp][hs[lcm]] != -1) return dp[pos][temp][hs[lcm]];    int up = limit ? a[pos] : 9;    int tmp, tlcm;    LL ans = 0;    for(int i=0;i<=up;i++){        tmp = (temp * 10 + i) % mod;        if(i){            tlcm = LCM(lcm, i);            ans = ans + DFS(pos-1, tmp, tlcm, limit && (i == up));        }        else ans = ans + DFS(pos-1, tmp, lcm, limit && (i == up));    }    if(!limit) dp[pos][temp][hs[lcm]] = ans;    return ans;}LL solve(LL n){    int num = 0;    mem(a, 0);    while(n){        a[num++] = n % 10;        n /= 10;    }    return DFS(num-1, 0, 1, true);}int main(){    int T;    LL l, r;    init();    mem(dp, -1);    scanf("%d", &T);    while(T--){        scanf("%lld%lld",&l, &r);        printf("%lld\n", solve(r) - solve(l-1));    }    return 0;}

HDU3555 Bomb

题意:
1-N到有多少个数是包含”49”的.

思路:
还记得不要62嘛…
emmmmm, 我们算一下不要49…然后哼哼哼…

LL dp[25][2];int a[25];LL DFS(int pos, int sta, bool limit){    if(pos < 0) return 1;    if(!limit && dp[pos][sta] != -1) return dp[pos][sta];    int up = limit ? a[pos] : 9;    LL ans = 0, t;    for(int i=0;i<=up;i++){        if(sta && i == 9) continue;        if(i == 4) t = 1;        else t = 0;        ans = ans + DFS(pos-1, t, limit && (i == up));    }    if(!limit) dp[pos][sta] = ans;    return ans;}LL solve(LL n){    int num = 0;    mem(a, 0);    while(n){        a[num++] = n % 10;        n /= 10;    }    return DFS(num-1, 0, true);}int main(){    int T;    LL n;    mem(dp, -1);    scanf("%d", &T);    while(T--){        scanf("%lld", &n);        printf("%lld\n", n - solve(n) + 1);    }    return 0;}

LightOJ 1140

题意:
给出一个区间,求区间内0出现的个数。
思路:
因为窝比较蠢,在想这个特殊位不就是0嘛,ok,状态设个 是0/不是0,感觉美滋滋。
但是显然,这就有一个重大的问题,是的没错,对于当前位,123456789这些数字,他的后多少位出现0的个数都没区别,
但是数位DP,这个DP记录的值表示的是对于当前位的前面所有情况!!!所以太明显了这个思路错的!
所以DP的状态 需要的是到当前位的一类情况
那么对于这道题,记录从首位到当前位零的个数就对了。

LL dp[25][15];int a[25];LL DFS(int pos, bool pre_zero, bool limit, int sta){    if(pos < 0) return (pre_zero ? 1 : (LL)sta);    if(!limit && !pre_zero && dp[pos][sta]!=-1) return dp[pos][sta];    int up = limit ? a[pos] : 9;    LL temp = 0;    for(int i=0;i<=up;i++){        if(pre_zero){            temp += DFS(pos-1, pre_zero&&i==0, limit&&i==up, sta);        }        else{            if(!i) temp += DFS(pos-1, pre_zero, limit && i==up, sta+1);            else temp += DFS(pos-1, pre_zero, limit && i==up, sta);        }    }    if(!limit && !pre_zero) dp[pos][sta] = temp;    return temp;}LL solve(LL x){    if(x<0) return 0;    if(x==0) return 1;    int num = 0;    while(x){        a[num++] = x % 10;        x /= 10;    }    return DFS(num-1, true, true, 0);}int main(){    LL n, m;    int T, cas = 1;    memset(dp, -1, sizeof(dp));    scanf("%d", &T);    while(T--){        scanf("%lld%lld", &n, &m);        printf("Case %d: ", cas++);        printf("%lld\n", solve(m) - solve(n-1));    }    return 0;}
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 小米4s微信反应很慢怎么办 手机网页缓存的视频播放不了怎么办 ios微信占用内存太大了怎么办 爱奇艺离线视频显示暂无缓存怎么办 酷狗音乐不小心删了歌怎么办 苹果手机里系统占的空间太大怎么办 酷狗音乐歌单里有不同步的歌怎么办 一插耳机手机自带音乐就响怎么办 清理空间时不小心把图片删了怎么办 手机中清理误把照片删了怎么办 清理手机文件把照片给删了怎么办 清理手机不小心把照片删了怎么办 金牛不回微信我也不理他他会怎么办 华为手机微信文件自动删除怎么办 微信清理数据后不能登录了怎么办 k歌占内存又不想删除歌曲怎么办 把所有商品放在一起做链接怎么办 微信解冻短信验证总显示失败怎么办 淘金币能抵钱商家拿了淘金币怎么办 真实订单被系统判定虚假交易怎么办 淘宝买家号疑似虚假交易违规怎么办 货品交易一方收了定金违约了怎么办 饿了么店铺收到差评怎么办 淘宝顾客退款没成功给差评怎么办 身份证绑定了淘宝注册支付宝怎么办 把钱转错到支付宝账号被扣了怎么办 网上买东西收到信息被删掉了怎么办 表格在手机上显示不出来怎么办? 电子表格中复制后没有虚线框怎么办 word中页眉页脚横线短了怎么办 亿图图示画的图不显示怎么办 掌柜宝用了几天无法登录了怎么办 手机千牛消息不小心删除了怎么办 淘宝账号被冻结提示无法恢复怎么办 商家未发货我误点了确认收货怎么办 淘宝买货商家不发货也不退款怎么办 商家没发货点成收货了怎么办 淘宝新店上架产品未通过审核怎么办 安卓手机电池虚电怎么办_查查吧 拼多多拼单期间商家下架商品怎么办 微信绑别人的卡需要人脸认证怎么办