数位DP习题

数位DP习题

      • 不要62 HDU - 2089(数位DP入门题)
      • Bomb HDU - 3555
      • B-number HDU - 3652
      • X mod f(x) HDU - 4389

不要62 HDU - 2089(数位DP入门题)

链接:http://acm.hdu.edu.cn/showproblem.php?pid=2089
题意:统计给定区间内,数位上没有4且连续两位上不存在62的数字个数
思路:从高位到低位扫,遇到 4 直接跳过,遇到 6 改变状态为 1 并判断下一个数字是否为 2

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+10;
int dp[20][2],digit[20];
int dfs(int pos,int sta,int lim)
{
    if(pos==0) return 1;
    if(!lim&&dp[pos][sta]!=-1) return dp[pos][sta];
    int up=lim?digit[pos]:9;
    int ans=0;
    for(int i=0; i<=up; ++i)
    {
        if(i==4||(sta==1&&i==2)) continue;
        ans+=dfs(pos-1,i==6,lim&&i==up);
    }
    if(!lim) dp[pos][sta]=ans;
    return ans;
}
int solve(int n)
{
    int cnt=0;
    while(n)
    {
        digit[++cnt]=n%10;
        n/=10;
    }
    return dfs(cnt,0,1);
}
int main()
{
    int l,r;
    memset(dp,-1,sizeof(dp));
    while(scanf("%d%d",&l,&r)&&(l||r))
        printf("%d\n",solve(r)-solve(l-1));
    return 0;
}

Bomb HDU - 3555

链接:http://acm.hdu.edu.cn/showproblem.php?pid=3555
题意:统计给定区间内,数位上连续两位上是49的数字个数
思路

  • 可以写成和上题一样,不要49的形式
  • 也可以正向考虑,最终形成了 49 才统计答案。一共有三种情况:不含49,含4不含9,包含 49。记录着三种状态,然后最后判断是否包含49即可。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+10;

int t;
ll n,dp[20][3];
int digit[20];
ll dfs(int pos,int sta,bool lim)
{
    if(pos==0) return sta==2;
    if(!lim&&dp[pos][sta]!=-1) return dp[pos][sta];
    int up=lim?digit[pos]:9;
    ll ans=0;
    for(int i=0; i<=up; ++i)
    {
        if(sta==2||(sta==1&&i==9))
            ans+=dfs(pos-1,2,lim&&i==up);
        else if(i==4)
            ans+=dfs(pos-1,1,lim&&i==up);
        else
            ans+=dfs(pos-1,0,lim&&i==up);
    }
    if(!lim) dp[pos][sta]=ans;
    return ans;
}
ll solve(ll n)
{
    int cnt=0;
    while(n)
    {
        digit[++cnt]=n%10;
        n/=10;
    }
    return dfs(cnt,0,1);
}
int main()
{
    memset(dp,-1,sizeof(dp));
    scanf("%d",&t);
    while(t--)
    {
        scanf("%lld",&n);
        printf("%lld\n",solve(n));
    }
    return 0;
}
//这是一段错误的统计代码,错在统计了不连续的4和9
if(sta==2||(sta==1&&i==9))
    ans+=dfs(pos-1,2,lim&&i==up);
if(sta==1||(sta==0&&i==4))
    ans+=dfs(pos-1,1,lim&&i==up);
if(sta==0&&i!=4)
    ans+=dfs(pos-1,0,lim&&i==up);

B-number HDU - 3652

链接:http://acm.hdu.edu.cn/showproblem.php?pid=3652
题意:给定一个 n n n [ 1 , n ] [1,n] [1,n] 中,存在连续两位为 13 并且整个数字能够内 13 整除的数字个数
思路:在需要 13 的基础上,记录连续位的和,并对13取模即可。

#include <bits/stdc++.h>
using namespace std;

int n;
int dp[10][3][15],digit[10];
int dfs(int pos,int sta,bool lim,int sum)
{
    if(pos==0) return sta==2&&sum%13==0;
    if(!lim&&dp[pos][sta][sum]!=-1) return dp[pos][sta][sum];
    int up=lim?digit[pos]:9;
    int ans=0;
    for(int i=0; i<=up; ++i)
    {
        if(sta==2||(sta==1&&i==3))
            ans+=dfs(pos-1,2,lim&&i==up,(sum*10+i)%13);
        else if(i==1)
            ans+=dfs(pos-1,1,lim&&i==up,(sum*10+i)%13);
        else
            ans+=dfs(pos-1,0,lim&&i==up,(sum*10+i)%13);
    }
    if(!lim) dp[pos][sta][sum]=ans;
    return ans;
}
int solve(int n)
{
    int cnt=0;
    while(n)
    {
        digit[++cnt]=n%10;
        n/=10;
    }
    return dfs(cnt,0,1,0);
}
int main()
{
    memset(dp,-1,sizeof(dp));
    while(~scanf("%d",&n))
        printf("%d\n",solve(n));
    return 0;
}

X mod f(x) HDU - 4389

链接:http://acm.hdu.edu.cn/showproblem.php?pid=4389

题意:给定区间 [ l , r ] [l,r] [l,r],询问区间内满足 x % f ( x ) = 0 x \% f(x)=0 x%f(x)=0 的数的个数, f ( x ) f(x) f(x) x x x 各个数位之和。 ( 1 ≤ x ≤ 1 0 9 ) (1\le x\le 10^9) (1x109)
思路:注意到模数最多只有 81 ,因此可以枚举模数,这样可以使 x 在 dfs 过程中不断取模控制在81以内,从而记忆化。

#include <bits/stdc++.h>
using namespace std;

int t;
int dp[10][85][85][85],digit[10];
int dfs(int pos,int x,int sum,int mod,bool lim)
{
    if(pos==0) return sum==mod&&x%mod==0;
    if(!lim&&dp[pos][x][sum][mod]!=-1) return dp[pos][x][sum][mod];
    int up=lim?digit[pos]:9;
    int ans=0;
    for(int i=0; i<=up; ++i)
        ans+=dfs(pos-1,(x*10+i)%mod,sum+i,mod,lim&&i==up);
    if(!lim) dp[pos][x][sum][mod]=ans;
    return ans;
}
int solve(int n)
{
    int cnt=0;
    while(n)
    {
        digit[++cnt]=n%10;
        n/=10;
    }
    int ans=0;
    for(int i=1; i<=81; ++i)
        ans+=dfs(cnt,0,0,i,1);
    return ans;
}
int main()
{
    int Case=0;
    scanf("%d",&t);
    memset(dp,-1,sizeof(dp));
    while(t--)
    {
        int l,r;
        scanf("%d%d",&l,&r);
        printf("Case %d: %d\n",++Case,solve(r)-solve(l-1));
    }
    return 0;
}

你可能感兴趣的:(动态规划,数位DP)