BZOJ 1799 浅谈数位动态规划再进阶

34 篇文章 0 订阅
9 篇文章 0 订阅

这里写图片描述
世界真的很大
感觉对于数位动态规划的计数问题还是有点眉目了
起码基本上模板的感觉是有了
剩下的就是状态设计的问题,设计怎么样的状态可以让搜索时得以保存?
开始研究。。

看题先:

description:

给出a,b,求出[a,b]中各位数字之和能整除原数的数的个数。

input:

10 19

output:

3

一看到这道题就直接想dfs
由于自己被什么什么整除,肯定DFS的时候要保存一个每一位的mod数,然后又要数位和,那就保存一个数位和
然后DFs很快就出来了

dnt dfs(int pos,int sum,int val,int lim)

sum代表数位和,val代表mod数
但忽然发现mod的数是数位和,不唯一,没法每次保存,而且在数位和本身算出来之前,没法mod
那就改变状态,val代表这个数是什么,在最后一位的时候判断能不能整除
那么这时大暴力搜索已经可以写出来了,现在再套上一个记忆化的DP数组就是数位DP了
考虑设计DP的状态,这个一般是根据dfs的参数来的,f(i,j,k)
表示数位为i,前几位和为j,val为k的答案
好像不大对。。

val保存的是前几位的状态的答案,也就是说,两次DFSval相同,当前仅当DFS枚举的前几位全部相同才行
而DFS每个数只会枚举一次。。这和暴力没什么区别。。
考虑为什么数位DP能够优化,是因为他提前记录了现在需要继续转移的“后继”状态,相当于是后几位的答案,在多次访问后继状态时实行时间的优化

但是val恰恰是指前几位的数是什么。。跟后继不沾边儿

考虑为什么val必须要保存前几位的数是什么,因为不同的数的mod数不同,没法单单记录一个整除的余数来转移,也就是说,问题出在不同的数的数位和不同,导致mod的数不同,没法记录一个公共状态

怎么办呢

每个数的mod数不同,就没法保持当前状态的“后继”状态的答案,这的却是个问题
但是反过来说,如果mod数确定,不就可以保存后继状态了吗?
看一下题目,这个mod的数究竟是什么,原来是数位和
10^18的范围,就是说有18位数位,考虑每一位最大都是9,数位和最多不过 【162】

~

好像思路显而易见了
我们考虑去枚举这样的数位和,不过枚举162次而已
问题就转化成了,数位和为i,且被i整除的数有多少
而这样一来,每次DFS时mod数相同,就可以递归mod的余数了,而一个余数加上后继的sum,表示的就是一堆后继的状态
和之前的f数组只会访问一次不同,这个可是多次访问的,记忆化成功

完整代码:

#include<stdio.h>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long dnt;

dnt f[20][170][170];
int a[20];

dnt dfs(int pos,int sum,int val,int mod,int lim)
{
    dnt ans=0;
    if(pos==-1) return sum==0&&val==0;
    if((pos+1)*9<sum) return 0; // 剪枝而已无须在意
    if(!lim && f[pos][sum][val]!=-1) return f[pos][sum][val];
    int up= lim ? a[pos] : 9;
    for(int i=0;i<=up;i++)
    {
        if(sum<i) break ;
        ans+=dfs(pos-1,sum-i,(val*10+i)%mod,mod,lim && i==up);
    }
    if(!lim) f[pos][sum][val]=ans;
    return ans;
}

dnt solve(dnt n)
{
    int cnt=0;
    dnt ans=0;
    memset(a,0,sizeof(a));
    while(n)
    {
        a[cnt++]=n%10;
        n/=10;
    }
    for(int i=1;i<=9*cnt;i++)
    {
        memset(f,-1,sizeof(f));
        ans+=dfs(cnt-1,i,0,i,1);
    }
    return ans;
}

int main()
{
    dnt a,b;
    cin >> a >> b;
    cout << solve(b)-solve(a-1) << endl;
    return 0;
}
/*
Whoso pulleth out this sword from this stone and anvil is duly born King of all England
*/

嗯,就是这样

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值