HDU 2089 数位 dp 入门

35 篇文章 0 订阅
2 篇文章 0 订阅

可暴力,可dp。

参考资料:Click Me~~

第二个版本的代码参考资料:7k+ 的板子

第二个版本代码在下面

做这道题的时候有两个疑问

第一:既然在dp中 51 既可以看做 51 也可以看做 0051 或者 000051 ,那么51这个数字会不会计算多次。

第二:既然状态转移保证了数字的合法,在统计的时候是否还要继续判断。

首先 dp 的意义 dp[i][j] 表示 i 长度 开头为 j 的数字的数量

关于第一个疑问。在dp中 0051 当然是在dp[3][0] dp[4][0] .. 中各计算一次。

但是我们统计的时候只统计了一次。

统计是这样的,我们只统计比 n 小的数字,先找最高位比 n 小的数字,比如 n=160, 051 这个数字最高位就小于他了,我们统计了一次,当我们统计到第2位的时候,这个意义是在最高位相同的情况下,这一位比 160 小的数字, 这样,我们统计的就是 151 这个数字,而不是051了,当然,每个数字都只统计了一次。

第二:dp[i][j] 只保证 j 开头 长度为i 的数字合法,不保证长度比他大的依然合法。。当然要判断一下。

#include <stdio.h>
#include <iostream>
#include <queue>
#include <algorithm>
#include <map>
#include <vector>
#include <cmath>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <fstream>
#include <set>
#include <stack>
using namespace std;

#define READ freopen("acm.in","r",stdin)
#define WRITE freopen("acm.out","w",stdout)
#define ll long long
#define ull unsigned long long 
#define PII pair<int,int>
#define PDI pair<double,int>
#define PDD pair<double,double>
#define MII map<int,int>::iterator 
#define fst first
#define sec second
#define MS(x,d) memset(x,d,sizeof(x))
#define INF 0x3f3f3f3f
#define ALL(x) x.begin(),x.end()
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define ROOT 0,n-1,1
#define PB push_back
#define FOR(a,b,c) for(int a=b;a<c;a++)
#define MOD 10007
#define keyTree (ch[ ch[root][1] ][0])
#define MAX 111

int dp[MAX][MAX];


int solve(int a)
{
    int len=0;
    int num[20];
    MS(num,0);
    while(a)
    {
        num[++len]=a%10;
        a/=10;
    }
    int ans=0;
    for(int i=len;i;i--)
    {
        for(int j=0;j<num[i];j++)
        {
            if(!(num[i+1]==6&&j==2))
              ans+=dp[i][j];
        }
        if(num[i+1]==6&&num[i]==2)
            return ans;
        if(num[i]==4)
            return ans;
    }
    return ans;
}
int main()
{
    dp[0][0]=1;
    for(int i=1;i<=8;i++)
    {
        for(int j=0;j<=9;j++)// i wei kai tou 
        {
            for(int k=0;k<=9;k++)// i-1 wei kaitou
            {
                if(j!=4&&(!(j==6&&k==2)))
                    dp[i][j]+=dp[i-1][k];
            }
        }
    }    
    int l,r;
    while(cin>>l>>r&&l&&r)
    {
        cout<<solve(r+1)-solve(l)<<endl;
    }
    return 0;
}

这个代码统计的是 <= n 的数字

#include <stdio.h>
#include <iostream>
#include <queue>
#include <algorithm>
#include <map>
#include <vector>
#include <cmath>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <fstream>
#include <set>
#include <stack>
using namespace std;

#define READ freopen("acm.in","r",stdin)
#define WRITE freopen("acm.out","w",stdout)
#define ll long long
#define ull unsigned long long 
#define PII pair<int,int>
#define PDI pair<double,int>
#define PDD pair<double,double>
#define MII map<int,int>::iterator 
#define fst first
#define sec second
#define MS(x,d) memset(x,d,sizeof(x))
#define INF 0x3f3f3f3f
#define ALL(x) x.begin(),x.end()
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define ROOT 0,n-1,1
#define PB push_back
#define FOR(a,b,c) for(int a=b;a<c;a++)
#define MOD 10007
#define keyTree (ch[ ch[root][1] ][0])
#define MAX 111

int dp[MAX][MAX];
int digit[MAX];
int len;

// dfs 在当前位是第 i 位(权重)数字为 s 的时候比 digit 小的数字有多少
int dfs(int i,int s,bool e)// i 是当前位 s 是当前位数字  dp[i][s]=sigma(valid dp[i-1][0-9]) e 为前一位是否是上界
{
    if(i==0)
        return s==0;
    if(!e&&dp[i][s]!=-1)
        return dp[i][s];
    int u=e?digit[i-1]:9;
    int res=0;
    for(int j=0;j<=u;j++)
    {
        if(j!=4&&!(s==6&&j==2))
            res+=dfs(i-1,j,e&&j==u);
    }
    return e?res:dp[i][s]=res;
}

int cal(int n)
{
    len=0;
    MS(digit,0);
    while(n)
    {
        digit[++len]=n%10;
        n/=10;
    }
    return dfs(len+1,0,1);
}


int main()
{
    MS(dp,-1);
    int l,r;
    while(cin>>l>>r&&l&&r)
        cout<<cal(r)-cal(l-1)<<endl;
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值