zoukankan      html  css  js  c++  java
  • 专题5

    数位dp

    数位dp其实可以理解为,对于给出的数字,对每一位数字进行讨论并状态转移,这边先给出一道题说明一下数位dp的整个过程。


    NC15035 送分了QAQ

    数字中不能出现(38)(4)。我们用(f[i][j])表示到第(i)位,状态为(j)时有多少个讨厌的数。其中状态仅有三种,状态(1)表示上一个数为(3),状态(2)表示已经出现过(4)或者(38),其余为状态(0)。用(dp(pos,st,flag))进行搜索,其中(pos)表示当前位置,(st)表示状态,(flag)表示数字选择是否受到限制(在前一位数字等于最大数时,当前位置的数字不能大于给出数字当前位置的数)。然后枚举当前位置的数字,当满足((i==4 || st==2 || (st==1 && i==8)),那么在(2)状态下进行搜索,如果(i==3),则转移至状态(1),否则转移至状态(0)。另外数字没有限制的情况必然会多次进行运算,所以在这里进行记忆化搜索。

    搜索部分的代码其实是大多数常规数位dp的模板,后面的很多题目都会用到。

    #include<bits/stdc++.h>
    #define ll long long
    #define pb push_back
    #define fast ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
    using namespace std;
    const int maxn = 10;
    int f[maxn][3];
    int a[maxn];
    ll ans1=0,ans2=0;
    int dp(int pos,int st,int flag)
    {
        if(pos==0) return st==2;
        if(flag && f[pos][st]!=-1) return f[pos][st];
        int x=flag?9:a[pos];
        int ans=0;
        for(int i=0;i<=x;i++)
        {
            if(i==4 || st==2 || (st==1 && i==8))
            {
                ans+=dp(pos-1,2,flag || i<x);
            }
            else if(i==3) ans+=dp(pos-1,1,flag || i<x);
            else ans+=dp(pos-1,0,flag || i<x);
        }
        if(flag) f[pos][st]=ans;
        return ans;
    }
    void init()
    {
        for(int i=1;i<=6;i++)
        {
            for(int j=0;j<3;j++)
            {
                f[i][j]=-1;
            }
        }
    }
    int main()
    {
        int n,m;
        init();
        while(cin>>n>>m)
        {
            if(n==0 && m==0) break;
            n--;
            int pos=0;
            while(n)
            {
                a[++pos]=n%10;
                n/=10;
            }
            ans1=dp(pos,0,0);
            pos=0;
            while(m)
            {
                a[++pos]=m%10;
                m/=10;
            }
            ans2=dp(pos,0,0);
            cout<<ans2-ans1<<'
    ';
        }
    }
    

    NC20665 7的意志

    题目中提到数字需要被(7,77,777,......,7777777)整除,不难发现其实这些数都能被(7)整除,之后的数字都是多余的;其次,数字的总和也需要是(7)的倍数。那我们就在搜索的同时记录当前的数字的模数与数位和模数,套用模板即可。

    #include<bits/stdc++.h>
    #define ll long long
    #define pb push_back
    #define fast ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
    using namespace std;
    int a[20];
    ll f[20][10][10];
    ll dp(int pos,int sum,int mod,int flag)
    {
        if(pos==0) return (sum%7==0 && mod%7==0);
        if(flag && f[pos][sum][mod]!=-1) return f[pos][sum][mod];
        int x=flag?9:a[pos];
        ll res=0;
        for(int i=0;i<=x;i++)
        {
            res+=dp(pos-1,(sum*10+i)%7,(mod+i)%7,flag || i<x);
        }
        if(flag) f[pos][sum][mod]=res;
        return res;
    }
    ll cal(ll x)
    {
        int pos=0;
        while(x)
        {
            a[++pos]=x%10;
            x/=10;
        }
        ll res=dp(pos,0,0,0);
        return res;
    }
    int main()
    {
        ll n,m;
        memset(f,-1,sizeof(f));
        while(cin>>n>>m)
        {
            if(n==0 && m==0) break;
            cout<<cal(m)-cal(n-1)<<'
    ';
        }
    }
    

    NC17385 Beautiful Numbers

    题目要求统计数字能被其数位和整除的数的个数。首先所有数字的数位和最多为(108),所以我们可以通过枚举数位和进行数位dp,同时统计数位和与模数即可。

    #include<bits/stdc++.h>
    #define ll long long
    #define pb push_back
    #define fast ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
    using namespace std;
    const int maxn = 15;
    ll f[maxn][110][110][110];
    int a[maxn];
    ll dp(int pos,int x,int mod,int sum,bool flag)
    {
        if(pos==0) return (x==sum && mod%sum==0);
        if(flag && f[pos][x][mod][sum]!=-1) return f[pos][x][mod][sum];
        ll d=flag?9:a[pos];
        ll ans=0;
        for(int i=0;i<=d;i++)
        {
            ans+=dp(pos-1,x,(mod*10+i)%x,sum+i,flag || i<d);
        }
        if(flag) f[pos][x][mod][sum]=ans;
        return ans;
    }
    ll calc(ll x)
    {
        int pos=0;
        while(x)
        {
            a[++pos]=x%10;
            x/=10;
        }
        ll res=0;
        for(int i=1;i<=108;i++)
        {
            res+=dp(pos,i,0,0,0);
        }
        return res;
    }
    int main()
    {
        fast;
        memset(f,-1,sizeof(f));
        int T;
        cin>>T;
        int cnt=0;
        ll n;
        while(T--)
        {
            cin>>n;
            cout<<"Case "<<++cnt<<": "<<calc(n)<<'
    ';
        }
    }
    

    CF55D Beautiful Numbers

    与上一题类似(连名字都一样),若能被数位的每一个数整除,一定能被数位的最小公倍数整除,所以我们可以枚举所有可能的公倍数,统计数字与(2520)(0-9)所有数字的最小公倍数)的模数,最后判断是否能被整除即可。

    #include<bits/stdc++.h>
    #define fast ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
    #define ll long long
    #define pb push_back
    using namespace std;
    const int maxn = 100010;
    ll dp[40][50][3000];
    ll lcm(ll a,ll b)
    {
        return a/__gcd(a,b)*b;
    }
    map<int,int> mp;
    vector<int> v;
    ll l,r;
    ll dfs(int pos,int sta,int mod,bool limit)
    {
        if(pos==-1) return mod%sta==0;
        if(!limit && dp[pos][mp[sta]][mod]!=-1)
        {
            return dp[pos][mp[sta]][mod];
        }
        int end=limit?v[pos]:9;
        ll ans=0;
        for(int i=0;i<=end;i++)
        {
            if(i!=0)
            {
                ans+=dfs(pos-1,lcm(sta,i),(mod*10+i)%2520,limit&&i==end);
            }
            else
            {
                ans+=dfs(pos-1,sta,(mod*10+i)%2520,limit&&i==end);
            }
        }
        if(!limit) dp[pos][mp[sta]][mod]=ans;
        return ans;
    }
    ll solve(ll x)
    {
        v.clear();
        while(x)
        {
            v.pb(x%10);
            x/=10;
        }
        return dfs(v.size()-1,1,0,1);
    }
    int main()
    {
        fast;
        int cnt=0;
        for(int i=1;i<=2520;i++)
        {
            if(2520%i==0) mp[i]=cnt++;
        }
        memset(dp,-1,sizeof(dp));
        int t;
        cin>>t;
        while(t--)
        {
            cin>>l>>r;
            // cout<<solve(r)<<' '<<solve(l-1)<<'
    ';
            cout<<solve(r)-solve(l-1)<<'
    ';
        }
    }
    
  • 相关阅读:
    php中读取文件内容的几种方法。(file_get_contents:将文件内容读入一个字符串)
    php标准库spl栈SplStack如何使用?
    js进阶正则表达式15验证身份证号(|符号的使用:var reg=/^d{17}[d|X]$|^d{15}$/)(str的方法substr)
    一步一步教你自定义博客园(cnblog)界面
    php中foreach源码分析(编译原理)
    php面试题12(多态web服务器共享session的方法:将session存到数据库)($val=&$data[$key];)
    前端开发必备调试工具(Chrome的F12自带的功能和firebug插件差不多)
    PHP 根据对象属性进行对象数组的排序(usort($your_data, "cmp");)(inside the class: usort($your_data, array($this, "cmp")))
    如何查看一个网页特定效果的js代码(动画效果可js和css)(页面可以看到js的源代码)
    bsh for android : 北京
  • 原文地址:https://www.cnblogs.com/endlesskkk/p/15470764.html
Copyright © 2011-2022 走看看