20201009day30 复习3:数位dp

LGP2657 [SCOI2009]windy数

problem

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

solution

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

数位dp

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

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

初始值:

\[dp_{1,i}=1(i\in[0,9],i\in\mathbb 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;
}
posted @ 2020-10-09 15:18  刘子闻  阅读(72)  评论(0编辑  收藏  举报