Digital dp learning

As the teacher has said before, let's review and deepen our understanding this time.

Digital DP sounds like dp, but it actually uses memory search (although they are essentially the same).
The idea is used to find the number of qualified numbers in a given interval. Conditions generally have nothing to do with the size of numbers, but with the composition of numbers.

Detailed explanation of Luogu Daily

The article's explanation is perfect, so I'll just go over it.

Examples:
HDU 2089 Do Not 62

Template questions, when searching, record the previous number to determine whether there are 6,2 adjacent. At the same time, determine whether the current position is 4.

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

int l,r,cnt;
int dp[20][20],num[20];

int dfs(int pos,int lead,int lim,int la)
{
	if(!pos) return 1;
	if(!lead&&!lim&&dp[la][pos]) return dp[la][pos];
	int l=1,up=lim ? num[pos]:9,sum=0;
	for(int i=0;i<=up;i++)
	{
		if(la==6&&i==2) continue;
		if(i==4) continue;
		if(lead&&!i) l=0;
		sum+=dfs(pos-1,l,lim&&(i==up),i);
	}
	if(!lead&&!lim) dp[la][pos]=sum;
	return sum;
}

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

int main()
{
	while(scanf("%d%d",&l,&r)&&l&&r)
	{
		printf("%d\n",work(r)-work(l-1));
	}
	return 0;
}
Luogu P2657 [SCOI 2009] Winy Number

Record the last number and determine if the difference between the current number and the previous number is greater than or equal to 2.

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

typedef long long ll;
ll t,l,r,len;
ll dp[20][20],num[20],ans[20];

ll dfs(ll pos,bool lim,bool lead,ll last)
{
	ll sum=0;
	if(pos==0) return 1;
	if(!lim&&lead&&dp[pos][last]) return dp[pos][last];
	int up=9;
	if(lim) up=num[pos];
	for(int i=0;i<=up;i++)
	{
		//ans[pos]=i;
		if(abs(i-last)<2) continue;
		ll l=i;
		if(!lead&&l==0) l=-10;
		sum+=dfs(pos-1,lim&&(i==up),lead||i,l);
	}
	if(!lim&&lead) dp[pos][last]=sum;
	return sum;
}

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

int main()
{
	//freopen("input.txt","r",stdin);
	scanf("%lld%lld",&l,&r); 
	printf("%lld",solve(r)-solve(l-1));
	return 0;
}
Luogu P2602 [ZJOI2010] Number Counting

Calculate the number of occurrences of each number separately. However, when calculating the number of occurrences of zero, it is necessary to judge whether there is a leading 0 or not. If there is a leading 0 and the current number is also zero, the answer is not included.

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

typedef long long ll;

ll a,b,len;
ll num[20],dp[20][20];

ll dfs(ll pos,ll lim,ll lead,ll dig,ll tot)
{
	ll sum=0;
	if(pos==0) return tot;
	if(!lim&&lead&&dp[pos][tot]) return dp[pos][tot];
	ll up=9;
	if(lim) up=num[pos];
	for(ll i=0;i<=up;i++)
	{
		sum+=dfs(pos-1,(i==up)&&lim,(i||lead),dig,tot+((i||lead)&&(i==dig)));
	}
	if(!lim&&lead) dp[pos][tot]=sum;
	return sum;
}

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

int main()
{
	//freopen("input.txt","r",stdin); 
	scanf("%lld%lld",&a,&b);
	for(ll i=0;i<=9;i++)
	{
		printf("%lld ",solve(b,i)-solve(a-1,i));
	}
	return 0;
}
Luogu P3413 SAC#1 - Number of germination

Key point: To make a number a palindrome substring, at least one digit for it is the same as the first digit or the first two digits of that digit (i.e. "the first one of the previous digits").
This is because there are two possibilities for symmetry: even number symmetry (self-named), such as two 1 symmetries in 2113, and odd number symmetry, such as two 1 symmetries in 21513, separated by a 5. Record the first two digits of the current bit for judgment while searching.
Note that when storing a new dp state value, in addition to judging the leading lead and upper lim, we also need to judge whether the last number is - 1 (that is, whether the last number has a leading or not). If the last number has a leading, we can not assign the value at this time, because the situation is different with or without the leading.
In addition, because the data is too large, I read in characters. Usually we want to solve (r) - solve (l-1), but we can't do L-1 at this time. In fact, after solving (r) - solving (l - 1), we can judge whether l is qualified.

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

typedef long long ll;
const int mod=1e9+7;
const int maxn=1005;
ll len,ans;
ll num[1005],dp[5][maxn][maxn];
char s[1005];

int dfs(int pos,int lead,int lim,int pre,int per,int ok)
{
	if(pos>len) return ok;
	//if(pos==len&&!ok) return 0;
	if(!lim&&!lead&&dp[ok][pre][pos]!=-1) return dp[ok][pre][pos];
	int up=lim ? num[pos]:9,sum=0,k;
	for(int i=0;i<=up;i++)
	{
		//k=!i&&lead ? -1:i;
		sum=(sum+dfs(pos+1,!i&&lead,lim&&(i==up),i,lead? -1:pre,ok||(i==pre&&!lead)||(i==per&&!lead)))%mod;
	}
	if(!lead&&!lim&&per!=-1) dp[ok][pre][pos]=(sum+mod)%mod;
	return (sum+mod)%mod;
}

int solve()
{
	len=strlen(s+1);
	memset(dp,0,sizeof(dp));
	for(int i=1;i<=len;i++) num[i]=s[i]-'0';
	memset(dp,-1,sizeof(dp));
	return dfs(1,1,1,-1,-1,0);
}

bool judge()
{
	int len=strlen(s+1);
	for(int i=1;i<=len;i++)
	{
		if(s[i]==s[i-1]) return 1;
		if(i>=3&&s[i]==s[i-2]) return 1;
	}
	return 0;
}

int main()
{
	//freopen("input.txt","r",stdin);
	scanf("%s",s+1);
	ans=solve()%mod;
	if(judge()) ans--;
	scanf("%s",s+1);
	ans=(solve()%mod-ans%mod)%mod;
	printf("%d",(ans+mod)%mod);
	return 0;
}
/*If at this time the search to the fifth place (leading 0 in - 1, not search for use? Representation)  
-1 -1  3  4   ?   ?   ?   ?   ?
1   2  3  4    ?   ?   ?   ?   ? The values of ok,pre,pos are the same, but the number of rows below*/
P4127 [AHOI2009] Similar Distribution

Record the sum of the preceding digits and the sum of all digits.
Sum's value is too large. Here we use modular operation to reduce its value. And just as we require dig to be divided by sum, it is not good to judge whether the answer is 0 after dig is taken as a module. Because the dig value is uncertain and its range is small, we enumerate the cumulative answer of mod. When the last number is searched, it can be judged whether mod is equal to dig and whether the sum after modular selection is 0.

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

typedef long long ll;
ll l,r,ans;
ll dp[20][200][200];
int cnt,num[20],mod;

ll dfs(ll pos,ll lead,ll lim,ll dig,ll sum)
{
	if(pos==0&&dig==0) return 0;
	if(pos==0) return dig==mod&&sum==0;
	if(!lead&&!lim&&dp[pos][dig][sum]!=-1) return dp[pos][dig][sum];
	int up=lim? num[pos]:9;
	ll res=0;
	for(int i=0;i<=up;i++)
	{
		res+=dfs(pos-1,lead&&!i,lim&&i==up,dig+i,((sum*10)+i)%mod);
	}
	if(!lead&&!lim) dp[pos][dig][sum]=res;
	return res;
}


ll solve(ll x)
{
	cnt=ans=0;
	while(x)
	{
		num[++cnt]=x%10;
		x/=10;
	}
	for(mod=1;mod<=9*cnt;mod++)
	{
		memset(dp,-1,sizeof(dp));
		ans+=dfs(cnt,1,1,0,0);
	}
	return ans;
}

int main()
{
	//freopen("input.txt","r",stdin);
	scanf("%lld%lld",&l,&r);
	printf("%lld",solve(r)-solve(l-1));
	return 0;
}

Posted by weyes1 on Wed, 21 Aug 2019 05:03:15 -0700