数位DP入门

数位DP入门

学习参考博客

直接上题:

专题II:
HDU-2089 不要62
参考博客例1即为这道题,这里主要对代码进行较详细的分析:

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
using namespace std;

typedef long long ll;

int a[20];
int dp[20][2];

int dfs(int pos,int pre,int sta,bool limit)
{
    if(pos==-1) return 1;
    if(!limit && dp[pos][sta]!=-1) return dp[pos][sta];
    int up=limit ? a[pos] : 9;
    int tmp=0;
    for(int i=0;i<=up;i++)
    {
        if(pre==6 && i==2)continue;
        if(i==4) continue;//都是保证枚举合法性
        tmp+=dfs(pos-1,i,i==6,limit && i==a[pos]);
    }
    if(!limit) dp[pos][sta]=tmp;
    return tmp;
}

int solve(int x)
{
    int pos=0;
    while(x)
    {

        a[pos++]=x%10;
        x/=10;
    }
    return dfs(pos-1,-1,0,true);
}

int main()
{
    int le,ri;
    //memset(dp,-1,sizeof dp);可优化
    while(~scanf("%d%d",&le,&ri) && le+ri)

    {
        memset(dp,-1,sizeof dp);
        printf("%d\n",solve(ri)-solve(le-1));
    }
    return 0;
}

这里详细理解变量sta的作用,此题的dp是记录一个位置后面的数位可能的所有情况数,我们可以先假设不用sta变量和limit变量:举个例子:当x=1000时,dfs首先搜百位是0 十位是0的情况,个位此时有0~ 5、7~ 9共9种可能,由于当pos = -1时dfs返回1,所以记录下来得到dp[0]=9,又因为十位数也有0~3、 5、 7~ 9共9种可能,记录下来得到dp [ 1 ]=81 ,很明显这个dp没有考虑题目所要求的不能出现62。
sta就起了将前一位数是6和前一个数不是6这两种情况分开记录的作用:加入sta后,百位是0,十位是0时,个位有9种可能,记录是用dp[0][1]=9,当十位是6时,个位的记录是用dp[0][0]=8。
所以到计算百位(此时百位为0)时,十位为0 ~ 3、5、7 ~ 9使用的是dp[0][1], tmp得到72,十位为6使用的是dp[0][0]=8得到tmp+8=80。所以得到dp[2][0]=80。
计算到百位为6时,十位有0 ~1、 3、5 ~ 9共8种选法(其中十位为6时dp[1][1]=8),得dp[2][1]=71;以此递推。

另一AC代码:


#include<iostream>

#include<cstdio>

#include<cstring>

#include<string>

using namespace std;

typedef long long ll;

int a[20];

int dp[20];

int dfs(int pos,int pre,int sta,bool limit)

{

    if(pos==-1) return 1;

    if(!limit && dp[pos]!=-1&& sta!=1 ) return dp[pos];
	if(!limit && dp[pos]!=-1&& sta==1 &&pos==0) return dp[pos]-1;
	if(!limit && dp[pos]!=-1&& sta==1 &&pos>=1) return dp[pos]-dp[pos-1];
    int up=limit ? a[pos] : 9;

    int tmp=0;

    for(int i=0;i<=up;i++)

    {

        if(pre==6 && i==2)continue;

        if(i==4) continue;//都是保证枚举合法性

        tmp+=dfs(pos-1,i,i==6,limit && i==a[pos]);

    }

    if(!limit) dp[pos]=tmp;

    return tmp;

}

int solve(int x)

{

    int pos=0;

    while(x)

    {

        a[pos++]=x%10;

        x/=10;

    }

    return dfs(pos-1,-1,0,true);

}

int main()

{

    int le,ri;

    //memset(dp,-1,sizeof dp);可优化

    while(~scanf("%d%d",&le,&ri) && le+ri)

    {

        memset(dp,-1,sizeof dp);

        printf("%d\n",solve(ri)-solve(le-1));

    }

    return 0;

}

又因为数据较小可以暴力a题:

#include<stdio.h>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<stack>
#define mod 1000000007
#define esp 1e-9
  
using namespace std;

int dp[1000005];
int main()
{
	int cnt=0;
	for(int i=0;i<=1000000;i++)
	{
		dp[i]=cnt;
		int cmp=i;
		while(cmp)
		{
			if(cmp%10==4){
				cnt++;
				dp[i]=cnt;
				break;
			}
			if(cmp%10==2&& (cmp/10)%10==6)
			{
				cnt++;
				dp[i]=cnt;
				break;
			}
			cmp/=10;
		}
	}
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
    	if(n==0&&m==0)break;
    	int res=(m-n+1)-(dp[m]-dp[n-1]);
    	printf("%d\n",res);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值