好朋友---数位dp

题目链接:
https://ac.nowcoder.com/acm/problem/19327

题目大意:
给你一个t,t次询问,每一次询问给你一个L,R,然后[L,R]中满足包含007(可以不连续),不含前导0的数字个数,把t次询问的结果异或起来就是答案

分析:
这道题也相对不是很难,但是有一个细节需要注意,怎么样去设置dp数组,以及维数的含义非常重要,这样可以避免重复的初始化而节省时间不超时,
我们设置dp数组为dp[pos][num][pre][res],pos表示当前位,num表示以及组成了007中多少个数字,例如我已经有00了,那么num就是2,已经有007了,那么num就是3,pre表示当前位的上一位数字,res表示剩下多少位没有枚举

细节:有人会问,为什么要多一个维数res用来记录还剩下多少位没有枚举,因为这样可以不用每一次都初始化dp数组,只需要开始初始化一次就可以了,这样节省了很多时间,如果不设置res这一维的话,那么会TLE,不相信可以试一试,所以设置dp维数的思想尤为重要.

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[20];
ll f[20][4][10][20];
/*pos表示当前位置,num表示取到在007的第几个数字了,pre表示当前位前一位的数字 
first处理前导0,res表示后面还剩多少位没有枚举*/
ll dfs(int pos,int limit,int num,int pre,int first,int res)
{
    
    
	if(!pos)
	{
    
    
		if(num==3) return 1;
		else return 0;
	}
	if(!limit&&~f[pos][num][pre][res]) return f[pos][num][pre][res];
	int ed = a[pos];
	if(!limit) ed = 9;
	ll ans = 0;
	for(int i=0;i<=ed;i++)
	{
    
    
		if(first&&!i)
		{
    
    
			ans+=dfs(pos-1,limit&&(i==ed),num,i,1,res-1);
			continue;
		}
		if(num==3)
		ans+=dfs(pos-1,limit&&(i==ed),3,i,0,res-1);
		else if((!num&&!i)||((num==1)&&!i)||((num==2)&&(i==7)))
		ans+=dfs(pos-1,limit&&(i==ed),num+1,i,0,res-1);
		else
		ans+=dfs(pos-1,limit&&(i==ed),num,i,0,res-1);
	}
	if(!limit) f[pos][num][pre][res] = ans;
	return ans;
}
ll solve(ll n)
{
    
    
	if(n<1007) return 0;
	memset(a,0,sizeof(a));
	
	int cnt = 0;
	while(n)
	{
    
    
	   a[++cnt] = n%10;
	   n/=10;	
	}
	ll ans = dfs(cnt,1,0,0,1,cnt-1);
	return ans; 
} 
int main()
{
    
    
	memset(f,-1,sizeof(f));
	int t;
	cin>>t;
	ll ans = 0;
	while(t--)
	{
    
    
	ll l,r;
	cin>>l>>r;
    ll temp = solve(r)-solve(l-1);
    ans^=temp;
	}
	cout<<ans<<'\n';
	return 0;
} 

おすすめ

転載: blog.csdn.net/TheWayForDream/article/details/116209474