20201009day30 复习3:数位dp

LGP2657 [SCOI2009]windy数

problem

([a,b])之间“(A)数”的个数。
一个数被称为“(A)数”,当且仅当它不含前导零且相邻两个数字之差至少为2.
(1le a le b le 2 imes 10^9)

solution

只需要规定函数work(x)([1,x])内所有的“(A)数”的个数,前缀和就好。

数位dp

(dp_{i,j})表示长度为(i)中最高位是(j)的“(A)数”
方程:

[dp_{i,j}=sum_{|j-k|ge 2}^{kin[0,9],kin mathbb Z} dp_{i-1,k} ]

初始值:

[dp_{1,i}=1(iin[0,9],iinmathbb Z) ]

code

#include<bits/stdc++.h>
using namespace std;
//设dp[i][j]为长度为i中最高位是j的windy数的个数
//方程 dp[i][j]=sum(dp[i-1][k]) 其中 abs(j-k)>=2 
int p,q,dp[15][15],a[15];
void init()
{
	for(int i=0;i<=9;i++)	dp[1][i]=1;	//0,1,2,3,4...9都属于windy数 
	for(int i=2;i<=10;i++)
	{
		for(int j=0;j<=9;j++)
		{
			for(int k=0;k<=9;k++)
			{
				if(abs(j-k)>=2)	dp[i][j]+=dp[i-1][k]; 
			}
		}
	}//从第二位开始 每次枚举最高位j 并找到k 使得j-k>=2 
}
int work(int x)	//计算<=x的windy数 
{
	memset(a,0,sizeof(a));
	int len=0,ans=0;
	while(x)
	{
		a[++len]=x%10;
		x/=10;
	}
	//分为几个板块 先求len-1位的windy数 必定包含在区间里的 
	for(int i=1;i<=len-1;i++)
	{
		for(int j=1;j<=9;j++)
		{
			ans+=dp[i][j];
		} 
	}
	//然后是len位 但最高位<a[len]的windy数 也包含在区间里 
	for(int i=1;i<a[len];i++)
	{
		ans+=dp[len][i];
	} 
	//接着是len位 最高位与原数相同的 最难搞的一部分 
	for(int i=len-1;i>=1;i--)
    {
    	//i从最高位后开始枚举 
        for(int j=0;j<=a[i]-1;j++)
		   {
		   	//j是i位上的数 
		   		if(abs(j-a[i+1])>=2)	ans+=dp[i][j]; //判断和上一位(i+1)相差2以上
				   //如果是 ans就累加 
		   } 
		if(abs(a[i+1]-a[i])<2)       break;
      //  if(i==1)   ans+=1;
    }
	return ans;
}
int main()
{
	init();
	cin>>p>>q;
	cout<<work(q+1)-work(p)<<endl;
	return 0;
}
要做就做南波万
原文地址:https://www.cnblogs.com/liuziwen0224/p/20201009day30-004.html