动态规划六

复健\(Day4\)

动态规划(六)数位\(DP\)

数位\(DP\)的特点:求某个区间\([l,r]\)内,满足某种性质的数的个数

技巧一:类似前缀和的思想,转化为\([0,r]-[0,l-1]\)求解

技巧二:从高位到低类填数,分类讨论;

比如比\(R\)小的数的个数,对于数\(R=a_{n}a_{n-1}\cdots a_n\),如果某一位填了\(0-a_i\),则后面每一位都可填\(0-9\)的任意数,如果填\(a_i\),则继续讨论下一位(这样才不会大于\(R\))

\(1.\)数字游戏

\(f[i][j]\)表示一共有\(i\)位,且最高位数字为\(j\)的不降数个数

\(f[i][j]=\sum_{k=j} ^{9} f[i-1][k]\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 12
using namespace std;

int a[maxn],f[maxn][maxn];

void init()
{
	for(int i=0;i<=9;i++) f[1][i]=1;
	for(int i=2;i<=maxn;i++)
	{
		for(int j=0;j<=9;j++)
		{
			for(int k=j;k<=9;k++) f[i][j]+=f[i-1][k];
		}
	}
}

int dp(int n)
{
	if(!n) return 1;//特判,0也是一个不降数 
	int cnt=0;
	while(n) a[++cnt]=n%10,n/=10;
	int res=0,last=0;
	for(int i=cnt;i>=1;--i)
	{
		int now=a[i];
		for(int j=last;j<now;j++) res+=f[i][j];
		if(now<last) break;
		last=now;
		if(i==1) res++;//就是0也是一个不降数 
	}
	return res;
}

int main()
{
	init();
	int l,r;
	while(cin>>l>>r)
	{
		cout<<dp(r)-dp(l-1)<<endl;
	}
	return 0;
}

\(2.\)\(windy\)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define maxn 10
using namespace std;

int f[maxn][maxn];

void init()
{
	for(int i=0;i<=9;i++) f[1][i]=1;
	for(int i=2;i<=10;i++)
	{
		for(int j=0;j<=9;j++)
		{
			for(int k=0;k<=9;k++)
			{
				if(abs(k-j)>=2) f[i][j]+=f[i-1][k];
			}
		}
	}
}

int dp(int n)
{
	if(!n) return 0;
	vector<int> a;
	while(n) a.push_back(n%10),n/=10;
	int last=-2;
	int res=0;
	for(int i=a.size()-1;i>=0;i--)
	{
		for(int j=(i==a.size()-1);j<a[i];j++)
		{
			if(abs(j-last)>=2) res+=f[i+1][j];
		}
		if(abs(a[i]-last)<2) break;
		last=a[i];
		if(!i) res++;
	}
	for(int i=a.size()-1;i>=1;i--)
	{
		for(int j=1;j<=9;j++) res+=f[i][j];
	}
	return res;
}

int main()
{
	int a,b;
	cin>>a>>b;
	init();
	printf("%d\n",dp(max(a,b))-dp(min(a,b)-1));
	return 0;
}

\(3.\)度的数量

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 12
using namespace std;

int a[maxn],f[maxn][maxn];
int k,B;

void init()//预处理组合数
{
	for(int i=0;i<=maxn;i++) f[i][0]=1;
	for(int i=1;i<=maxn;i++)
	{
		for(int j=1;j<=i;j++)
		{
			f[i][j]=f[i-1][j-1]+f[i-1][j];
		}
	}
}

int dp(int n)
{
	if(!n) return 0;//特判,0也是一个不降数 
	int cnt=0;
	while(n) a[++cnt]=n%B,n/=B;
	int res=0,last=0;
	for(int i=cnt;i>=1;--i)
	{
		int now=a[i];
		if(now)
		{
			res+=f[i-1][k-last];
			if(now>1)
			{
				if(k-last-1>=0) res+=f[i-1][k-last-1];
				break;
			}
			else
			{
				last++;
				if(last>k) break;
			}
		}
		if(i==1&&last==k) res++;
	}
	return res;
}

int main()
{
	init();
	int l,r;
	cin>>l>>r>>k>>B;
	cout<<dp(r)-dp(l-1)<<endl;
	return 0;
}

posted on 2023-08-03 14:51  dolires  阅读(29)  评论(0)    收藏  举报

导航