动态规划之数位动规

1.BSOJ - 1726 - 【模拟试题】准考证

#include<bits/stdc++.h>
using namespace std;
/*
题意:求给出的A和B之间没有4和连续的37的数字的个数
1<=A<=B<=2000000000 
*/ 
int f[11][11]={0};
int solve(int x)//从小到大考虑 
{
	int a[11]={0};//分解x 
	while(x)a[++a[0]]=x%10,x/=10;
	
	int ret=0;
	for(int i=1;i<a[0];i++)//枚举位数,不足a[0]位 
	for(int j=1;j<=9;j++)ret+=f[i][j];//枚举最高位数字为j 
	for(int i=1;i<a[a[0]];i++)ret+=f[a[0]][i];//最高位不足a[a[0]] 
	if(a[a[0]]==4)return ret;
	for(int i=a[0]-1;i>=1;i--)//从次高位往下 
	{
		//枚举第i位数字为j,依题意和第i+1位比较 
		for(int j=0;j<a[i]+(i==1);j++)if(j!=4&&!(a[i+1]==3&&j==7))ret+=f[i][j];
		//最大的数字已出现不合法情况则直接返回 
		if(a[i]==4||(a[i+1]==3&&a[i]==7))return ret;
	}
	
	return ret;
}
int main()
{
	//f[i][j]表示i位数字,首位数字为j的情况总数 
	
	for(int i=0;i<=9;i++)if(i!=4)f[1][i]=1;//初始化1位的情况 
	
	for(int i=2;i<=10;i++)//枚举位数 
	for(int j=0;j<=9;j++)//枚举首位//注意在初始化f的过程中前导0是允许的 
	for(int k=0;k<=9;k++)//枚举次位 
	{
		if(j!=4&&k!=4&&!(j==3&&k==7))f[i][j]+=f[i-1][k];
	}
	
	int x,y;
	scanf("%d%d",&x,&y);
	printf("%d\n",solve(y)-solve(x-1));//注意边界值x也要考虑 
	return 0;
}

 

2.BSOJ - 2159 - 【SCOI2007】排列

#include<bits/stdc++.h>
using namespace std;
/*
题意:求区间[X,Y]内恰好等于K个互不相等的B的整数次幂之和的数的个数
数据范围:1<=X<=Y<=2^31-1   1<=K<=20   2<=B<=10
思路:
把询问的数转换成B进制xb,则要求的数为每个B进制数位上有K个1且小于xb的数
设f(i,j,k)表示第i位,首位为j,已经用了k个1的数的个数 
*/
int X,Y,K,B,a[35]={0},f[35][2][25]={0};
int cal(int x)
{
	int ret=0;a[0]=0;
	while(x)a[++a[0]]=x%B,x/=B;//把询问的数转换成B进制 
	
	for(int i=1;i<=a[0]-1;i++)ret+=f[i][1][K];//枚举位数小于a的数 
	
	if(a[a[0]]>1)return ret+f[a[0]][1][K];//最高位已经大于1了,就直接返回了 
	
	int sum=1;//从高位推到低位时,已经用了的1的个数 
	
	for(int i=a[0]-1;i>=1;i--)
	{
		if(a[i]==1)
		{
			ret+=f[i][0][K-sum];//当前位为1则可以加上小于当前位的所有情况 
			sum++;//把当前位为1的情况加上 
		}
		//超过1则包含所有情况 
		else if(a[i])return ret+f[i][0][K-sum]+f[i][1][K-sum];
		
		if(i==1)ret+=f[i][0][K-sum];//???
	}
	
	return ret;
}
int main()
{
	scanf("%d%d%d%d",&X,&Y,&K,&B);
	
	//初始化 
	f[1][0][0]=1;//1位,首位为0、用了0个1的数有一个 
	f[1][1][1]=1;//1位,首位为1、用了1个1的数有一个 
	
	for(int i=2;i<=30;i++)//枚举B进制数,每个数位全为1或0的数的位数 
	for(int j=0;j<=1;j++)//枚举首位为0或1 
	for(int k=0;k<=K;k++)//枚举已经用了的1的个数 
	for(int t=0;t<=1;t++)//枚举上一个状态已经用了的1的个数 
		if(k-j>=0)f[i][j][k]+=f[i-1][t][k-j];
	
	cout<<cal(Y)-cal(X-1)<<endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/konjacrz/article/details/82805863