数位DP数位小孩

Posted 行码棋

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数位DP数位小孩相关的知识,希望对你有一定的参考价值。

题目链接:

https://ac.nowcoder.com/acm/contest/23480/D

[ l , r ] [l,r] [l,r]区间内满足以下条件数的个数:
1.任意相邻的两个数位之和为素数
2.数位中至少有一个1
3.没有前导0


本题由于l,r范围只有 1 0 10 10^10 1010,可以dfs暴力做,但是为了复习数位DP,下面用数位DP做。

数位DP经典先预处理f数组

  • 状态表示:
    f [ i ] [ j ] [ 1 ] f[i][j][1] f[i][j][1]: 满足相临数位之和为素数情况下,共有i位数,最高位为j,且数位中存在1的数的个数
    f [ i ] [ j ] [ 0 ] f[i][j][0] f[i][j][0]: 满足相临数位之和为素数情况下,共有i位数,最高位为j,且数位中存在1的数的个数

  • 状态转移:
    j=1
    f [ i ] [ j ] [ 1 ] + = f [ i − 1 ] [ k ] [ 0 ] + f [ i − 1 ] [ k ] [ 1 ] f[i][j][1] += f[i-1][k][0] + f[i-1][k][1] f[i][j][1]+=f[i1][k][0]+f[i1][k][1]
    f [ i ] [ j ] [ 0 ] = 0 f[i][j][0] = 0 f[i][j][0]=0
    j!=1
    f [ i ] [ j ] [ 1 ] + = f [ i − 1 ] [ k ] [ 1 ] f[i][j][1] += f[i-1][k][1] f[i][j][1]+=f[i1][k][1]
    f [ i ] [ j ] [ 0 ] + = f [ i − 1 ] [ k ] [ 0 ] f[i][j][0] += f[i-1][k][0] f[i][j][0]+=f[i1][k][0]

注意初始化f数组时, f [ 1 ] [ 0 ] [ 0 ] f[1][0][0] f[1][0][0]要初始化为1,虽然不满足为素数,但是需要初始化为1,因为后面比如说转移到20(2位数,最高位为2,不存在1,满足相邻数位之和为素数情况)时需要用到。

然后就是转移时,需要额外标记高位是否出现过1,本题用ok标记,如果出现过,要额外进行res += f[i+1][j][0]的操作

最后要加上位数小于给定的数的情况(位数小,整体的数肯定小了)


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

int f[20][20][2];

bool check(int a,int b)

	if(b < 0) return true;
	if(a+b==2||a+b==3||a+b==5||a+b==7||a+b==11||a+b==13||a+b==17) return true;
	return false;

void init()

	f[1][0][0] = 1;
	f[1][1][1] = 1;
	for(int i=2;i<=9;i++) f[1][i][0] = 1;
	for(int i=2;i<=10;i++)
		for(int j=0;j<=9;j++)
			for(int k=0;k<=9;k++)
			
				if(check(j,k))
				
					if(j==1)
					
						f[i][j][1] += f[i-1][k][0] + f[i-1][k][1];
						f[i][j][0] = 0;
						
					
					else  
					
						f[i][j][1] += f[i-1][k][1];
						f[i][j][0] += f[i-1][k][0];
					
				
			

ll dp(ll x)

	if(!x) return 0;
	
	vector<int>nums;
	while(x) nums.push_back(x%10),x/=10;
	
	int last = -1;
	ll res = 0;
	bool ok = false;
	
	for(int i=nums.size()-1;i>=0;i--)
	
		int c = nums[i];
		for(int j=(i==nums.size()-1);j<c;j++)
		
			if(check(j,last))
			
				res += f[i+1][j][1];
				if(ok) res += f[i+1][j][0];
			
		
		
		if(c==1) ok = true;
		
		if(check(c,last)) last = c;
		else break;
		
		if(!i and ok)  res ++;
	
	
	for(int i=1;i<nums.size();i++)
		for(int j=1;j<=9;j++)
			res += f[i][j][1];
	return res;

void solve()

	init();
	
	ll l,r;
	cin>>l>>r;
	cout<<dp(r)-dp(l-1)<<"\\n";

int main()

//	ios::sync_with_stdio(false);
//	cin.tie(0),cout.tie(0);
	int t;
//	cin>>t;
	t = 1;
	while(t--) solve();
	return 0;
  

以上是关于数位DP数位小孩的主要内容,如果未能解决你的问题,请参考以下文章

The 2018 ACM-ICPC上海大都会赛 J Beautiful Numbers (数位DP)

BZOJ 1799 同类分布(数位DP)

AHOI2009同类分布 题解(数位DP)

HDU 5898 (数位DP)

bzoj1799 数位dp

BZOJ1799 self 同类分布 数位dp