TJOJ 4148 (数位dp)

zjhl2 posted @ 2016年6月30日 21:50 with tags 数位DP , 372 阅读

题意:问1到n(1<=n<=10^8)所有的数,总共有多少个0出现。

做法:

很明显的数位dp

数位dp的题目我发现都可以用暴搜+记忆话过掉,而且写起来超级方便。赛场上第一个AC这道题。

 

暴搜是这么写的

#include<cstdio>
#include<cstring>
using namespace std;
char s[100];
int a[100];
int n;
long long get(int pos,bool up,bool zr,int now)
{
	if (pos>n) return now;
	long long ret=0;
	for (int i=0;i<=9;i++)
	{
		if (up&&i>a[pos]) continue;
		bool uup=up;
		if (i<a[pos]) uup=0;
		bool zzr=zr;
		if (i!=0) zzr=0;
		int nnow=now;
		if (i==0&&!zr) nnow++;
		ret+=get(pos+1,uup,zzr,nnow);
	}
	return ret;
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%s",s+1);
		n=strlen(s+1);
		for (int i=1;i<=n;i++) a[i]=s[i]-'0';
		printf("%lld\n",get(1,1,1,0));
	}
}

加个记忆化复杂度就变成了f数组的复杂度

#include<cstdio>
#include<cstring>
using namespace std;
char s[100];
int a[100];
int n;
long long f[100][2][2][100];
long long get(int pos,bool up,bool zr,int now)
{
	if (pos>n) return now;
	long long ret=f[pos][up][zr][now];
	if (ret!=-1) return ret;
	ret=0;
	for (int i=0;i<=9;i++)
	{
		if (up&&i>a[pos]) continue;
		bool uup=up;
		if (i<a[pos]) uup=0;
		bool zzr=zr;
		if (i!=0) zzr=0;
		int nnow=now;
		if (i==0&&!zr) nnow++;
		ret+=get(pos+1,uup,zzr,nnow);
	}
	return f[pos][up][zr][now]=ret;
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%s",s+1);
		n=strlen(s+1);
		for (int i=1;i<=n;i++) a[i]=s[i]-'0';
		memset(f,-1,sizeof(f));
		printf("%lld\n",get(1,1,1,0));
	}
}

数位dp的套路就是这样,暴搜加记忆化,超级简单。


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter