2023河南萌新联赛第(四)场 L.7是大奖?(数位DP基础)

文章目录

  • 题目大意
  • 题解
  • 参考代码
  • 总结

题目大意

2023河南萌新联赛第(四)场 L.7是大奖?(数位DP基础)_第1张图片
( 1 ≤ l , r ≤ 1 0 18 ) (1\leq l,r\leq 10^{18}) (1l,r1018)

题解

由题目可得
①:统计数字出现次数;
②:直接暴力计算无法得出;
③:输入给定区间。
满足使用数位DP的条件。
tip:
如果我们暴力求解会发现有许多计算重复,数位DP可以帮助我们运用重复计算的部分。
我们把 a n s l , r ans_{l,r} ansl,r 转化为 a n s 1 , r − a n s 1 , l − 1 ans_{1,r}-ans_{1,l-1} ans1,rans1,l1
去除一个维度,就成为了一个单调上升的函数。
其核心思想是枚举数位,搜索过程中记忆化。
数位DP学习
统计 5 5 5 7 7 7 的个数是朴素的数位DP,统计连续的 7 7 7 7 7 7
我们可以加入一个变量统计连续的 7 7 7 的个数。
详细可以看代码。

参考代码

#include
#define ll long long
using namespace std;
const int N=50000;
ll dp[20][50][50][20],num[20];
//len表示当前在第几位,limit表示是否贴着上限,lead表示是否有前导零
//sum5统计5的数量,sum7统计7的数量,siz统计连续的个数
ll dfs(int len,bool limit,bool lead,int sum5,int sum7,int siz)
{
	ll ans=0;
    if(len==0)
        return sum5+sum7*3+(siz>=7)*300;
	if(!limit && lead && dp[len][sum5][sum7][siz]!=-1)    //记忆化
		return dp[len][sum5][sum7][siz];
	int res=limit==1?num[len]:9;           //贴上限
	for(int i=0;i<=res;i++)
	{
		if(i!=5 && i!=7)
			ans+=dfs(len-1,limit&&i==res,lead||i,sum5,sum7,siz>=7?7:0);
		if(i==5)
			ans+=dfs(len-1,limit&&i==res,lead||i,sum5+1,sum7,siz>=7?7:0);
		if(i==7)
			ans+=dfs(len-1,limit&&i==res,lead||i,sum5,sum7+1,siz>=7?7:siz+1);
		//特殊的,如果已经有7个7,不需要考虑了,必定加+300
		//不然需要从0开始重新统计
	}
	if(!limit&&lead)            //没有任何限制再加入
		dp[len][sum5][sum7][siz]=ans;
	return ans;	
}
ll work(ll x)
{
	int len=0;
	while(x)            //取数位
	{
		num[++len]=x%10;
		x/=10;
	}
	return dfs(len,1,0,0,0,0);
}
int main()
{
	int T;
	ll l,r;
    memset(dp,-1,sizeof dp);
	cin>>T;
	while(T--)
	{
		scanf("%lld%lld",&l,&r);
		ll ans=(work(r)-work(l-1));
		printf("%lld\n",ans);
	}
}

对于为什么要把 s u m 5 、 s u m 7 、 s i z sum_5 、sum_7、siz sum5sum7siz 写进 DP,
当前的状态可能由不同个上一位转移而来,上一位是 5 5 5 ,和上一位是 7 7 7 的结果显然不同,
所以需要对不同的状态进行分开计算。

总结

数位DP模板题,经验++。

你可能感兴趣的:(c++)