数位DP从入门到入门

数位DP本质:

记忆化搜索

基本模板:

int dfs(int pos,int limit,int lead,int dig,int sum)
{
	int ans=0;
	if(pos==0)return sum;
	if(!limit&&lead&&dp[pos][sum]!=-1) return dp[pos][sum];
	int up=9;
	if(limit==1)up=num[pos];
	for(int j=0;j<=up;j++)
	{
		int flag=1;
		if(lead==0 && j==0)flag=0;
		ans+=dfs(pos-1,limit==1 && (j==up) ,flag,dig,sum+(flag && (j==dig)));
	}
	if(!limit&&lead) dp[pos][sum]=ans;
	return ans;
}

其中
p o s pos pos代表搜索到的位置(从高到低)
l i m i t limit limit表示当前 p o s pos pos可不可以取0~9( l i m i t limit limit==1表示不能从0 ~ 9,只能从0~ n u m [ p o s ] num[pos] num[pos]
l e a d lead lead表示是否有前导0(主要是用来判断0的情况,在某些题目里面可以省略)
d i g dig dig表示当前找的是哪个数
s u m sum sum表示从最高位到 p o s pos pos位符合条件的个数

一些题目:

Luogu P2602 [ZJOI2010]数字计数
#include <bits/stdc++.h>
#define ll long long
using namespace std;
template<typename t>void read(t &x)
{
    x=0;ll  f=1;char ch=getchar();
    while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
    x=x*f;
}
ll a,b,l,r;
ll dp[20][20],num[20];
void init()
{
	freopen("Lg P2602.in","r",stdin);
}

ll dfs(ll pos,ll limit,ll lead,ll dig,ll sum)
{
	ll ans=0;
	if(pos==0)return sum;
	if(!limit&&lead&&dp[pos][sum]!=-1) return dp[pos][sum];
	ll up=9;
	if(limit==1)up=num[pos];
	for(ll j=0;j<=up;j++)
	{
		ll flag=1;
		if(lead==0 && j==0)flag=0;
		ans+=dfs(pos-1,limit==1 && (j==up) ,flag,dig,sum+(flag && (j==dig)));
	}
	if(!limit&&lead) dp[pos][sum]=ans;
	return ans;
}


ll solve(ll x,ll digit)
{
	memset(dp,-1,sizeof(dp));
	ll len=0;
	while(x)
	{
		num[++len]=x%10;
		x=x/10;
	}
	dfs(len,1,0,digit,0);
}

void readdata()
{
	read(l),read(r); 
}

void work()
{
	for(ll i=0;i<=9;i++)
	{
		printf("%lld ",solve(r,i)-solve(l-1,i));
	} 
}

int main()
{
//	init();
	readdata();
	work();
	return 0;
} 

当然也可以找规律

#include <bits/stdc++.h>
#define ll long long
using namespace std;
template<typename t>void read(t &x)
{
    x=0;ll f=1;char ch=getchar();
    while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
    x=x*f;
}
ll lef[1005],rit[1005];
ll l,r,x,m;
void init()
{
    freopen("Lg P2602.in","r",stdin);
}

void readdata()
{
    read(l),read(r);
    l=l-1;
}

void work()
{
    for(ll t=0;t<=9;t++)
    {
        x=t;
        m=1;
        while(m<=l)
        {
            ll a=l/(m*10),b=l/m%10,c=l%m;
            if(x)
            {
        //		755
                if(b>x) lef[t]+=(a+1)*m;
                if(b==x)lef[t]+=a*m+c+1;
                if(b<x) lef[t]+=a*m;
            }
        //	0705
            else 
            {
                if(b) lef[t]+=a*m;
                else  lef[t]+=(a-1)*m+c+1;
            }
            m=m*10;
        }
        
    }
    for(ll t=0;t<=9;t++)
    {
        x=t;
        m=1;
        while(m<=r)
        {
            ll a=r/(m*10),b=r/m%10,c=r%m;
            if(x)
            {
            //	755
                if(b>x) rit[t]+=(a+1)*m;
                if(b==x)rit[t]+=a*m+c+1;
                if(b<x) rit[t]+=a*m;
            }
            else 
            {
                if(b) rit[t]+=a*m;
                else rit[t]+=(a-1)*m+c+1; 
            }
            m=m*10;
        }
    }
    for(ll i=0;i<=9;i++)
    {
        printf("%lld ",rit[i]-lef[i]);
    }
}


int main()
{
//	init();
    readdata();
    work();
    return 0;
}


Luogu P2657 [SCOI2009]windy数

在DFS内部特判即可

#include<bits/stdc++.h>
using namespace std;
template<typename t>void read(t &x)
{
    x=0;int f=1;char ch=getchar();
    while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
    x=x*f;
}
int dp[20][20],num[20];
int l,r;
int dfs(int pos,int limit,int lead,int last)
{
	int ans=0;
	if(pos==0)return 1;
	if(limit==0 && lead==1 && dp[pos][last]!=-1)return dp[pos][last];
	int up=9;
	if(limit)up=num[pos];
	for(int j=0;j<=up;j++)
	{
		if(abs(j-last)<2)continue;
		int flag=1,lastt=j;
		if(lead==0 && j==0)flag=0;
		if(flag==0)lastt=-2333;
		ans+=dfs(pos-1,limit && (up==j),flag,lastt);
	}
	if(limit==0 && lead==1)dp[pos][last]=ans;
	return ans;
	
}

int solve(int x)
{
	memset(dp,-1,sizeof(dp));
	int tot=0;
	while(x)
	{
		num[++tot]=x%10;
		x=x/10;
	}
	return dfs(tot,1,0,-2333);
}

void init()
{
	freopen("Lg P2657.in","r",stdin);
}

void readdata()
{
	read(l),read(r);
}

void work()
{
	printf("%d",solve(r)-solve(l-1));
}

int main()
{
//	init();
	readdata();
	work();
	return 0;
} 
Loj #10164. 「一本通 5.3 例 2」数字游戏
#include<bits/stdc++.h>
using namespace std;
int num[200],dp[200][200];
int l,r;
int dfs(int pos,int limit,int last)
{
	int ans=0;
	if(pos==0)return 1;
	if(limit==0 && dp[pos][last]!=-1)return dp[pos][last];
	int up=9;
	if(limit)up=num[pos];
	for(int i=last;i<=up;i++)
	{
		ans+=dfs(pos-1,limit && (i==up),i);
	}
	if(limit==0)dp[pos][last]=ans;
	return ans;
}

int solve(int x)
{
	memset(dp,-1,sizeof(dp));
	int tot=0;
	while(x)
	{
		num[++tot]=x%10;
		x=x/10;
	}
	return dfs(tot,1,0);
}

void init()
{
	freopen("Loj 10164.in","r",stdin);
}

void readdata()
{
	while(scanf("%d %d",&l,&r)!=EOF)
	{
		printf("%d\n",solve(r)-solve(l-1));
	}
}

int main()
{
	//init();
	readdata();
	return 0;
}
Loj #10166. 「一本通 5.3 练习 1」数字游戏
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll num[200];
ll dp[200][200];
ll a,b,mod;
ll dfs(ll pos,ll limit,ll judge)
{
	ll ans=0;
	if(pos==0)
	{
		if(judge==0)return 1;
		else return 0;
	}
	if(limit==0 && dp[pos][judge]!=-1)return dp[pos][judge];
	ll up=9;
	if(limit)up=num[pos];
	for(ll i=0;i<=up;i++)
	{
		ans+=dfs(pos-1,limit && (i==up),(judge+i)%mod);
	}
	if(limit==0)dp[pos][judge]=ans;
	return ans;
}

ll solve(ll x)
{
	memset(dp,-1,sizeof(dp));
	ll tot=0;
	while(x)
	{
		num[++tot]=x%10;
		x=x/10;
	}
	return dfs(tot,1,0); 
}

void init()
{
	freopen("Loj10166.in","r",stdin);
}

void readdata()
{
	while(scanf("%d%d%d",&a,&b,&mod)!=EOF)
	{
		printf("%lld\n",solve(b)-solve(a-1));
	}
}

int main()
{
//	init();
	readdata();
	return 0;
}
Loj #10167. 「一本通 5.3 练习 2」不要 62
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll num[20];
ll dp[10][2][10];
ll l,r;
ll dfs(ll pos,ll limit,ll type,ll pre)
{
	if(pos==0)return type;
	if(!limit && dp[pos][type][pre]!=-1)return dp[pos][type][pre];
	ll up=9,ans=0,nexttype=0;
	if(limit)up=num[pos];
	for(ll i=0;i<=up;i++)
	{	
		nexttype=0;
		if(type||i==4||(i==2&&pre==6))nexttype=1;
		ans+=dfs(pos-1,limit && (up==i),nexttype,i);
	}
	if(limit==0)dp[pos][type][pre]=ans;
	return ans;
}

ll solve(ll x)
{
	memset(dp,-1,sizeof(dp));
	ll tot=0;
	while(x)
	{
		num[++tot]=x%10;
		x=x/10;
	}
	return dfs(tot,1,0,0);
}

void init()
{
	freopen("Loj10167.in","r",stdin);
}

void readdata()
{
	while(scanf("%d%d",&l,&r)!=EOF)
	{
		if(l==0 && r==0)break;
		printf("%d\n",r-l+1-(solve(r)-solve(l-1)));
	}
}

int main()
{
//	init();
	readdata(); 
	return 0;
 } 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值