数位dp。

这篇具有很好参考价值的文章主要介绍了数位dp。。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一,思想:

在处理1e9甚至1e18,1e100的问题时,因为在统计情况下有很多重复的计算,数位dp实现了相同状态只计算一次,从而大幅减少运算时间,思想就是对每一位进行dp,计算时记忆化每一位可以有的状态。

如我们在统计1234的状态时,可以拆成统计0~10000,0~2000,0~300,0~40数位统计

我们用bit数组由低到高存储每一位,bit[1]=4,bit[2]=3,bit[3]=2,bit[4]=1.

然后dp从高位到低位进行文章来源地址https://www.toymoban.com/news/detail-430067.html

const int N = 20;
int dp[20][N],bit[N];
int dfs(int len,int sta,bool limit)//limit表示当前位有没有被bit限制
{
	if(!len)return (sta==1);//进行到最后一位,判断状态是否符合情况,符合就+1
	if(!limit&&dp[len][sta]!=-1)return dp[len][sta];//如果记忆化过,直接返回
	int ans=0;
	int top=limit?bit[len]:9;//如果有限制,如1234,当前len=4,那么top<=bit[4]=1,有限制,没有limit限制就可以0~9
	for(int i=0; i<=top; ++i)if(状态)ans+=dfs(len-1,newsta,limit&&i==top);//累积符合状态的情况
	if(!limit)return dp[len][sta]=ans;//如果没有限制,即我们现在计算出来符合该状态的所有情况数(没有被限制的)那么可以记忆下来
	return ans;
}

int cal(int x)//把数字x按位存储到数组
{
	int len=0;
	while(x)bit[++len]=x%10,x/=10;
	return dfs(len,0,1);
}

注意事项:

  1. 前导0问题,在数位dp中,0,000,0000是看成不同的,所以统计答案需要考虑他们是否有影响
  2. 继承问题,如从高位到低位计算0000123,前面4个0是初始状态,有时继承时需要特判考虑

例题1,Problem - 4507 (hdu.edu.cn)

思路:

  1. 对于数位dp求取与7无关的数,操作是简单的。无脑模拟即可
    1. 首先显然的,我们每一位没有7
    2. 然后他的状态就是前面各位的累积和模7,前面的数模7。如果当前len位时,前面的累积(dsum)模7相等且前面的数字(ssum)模7也相等,可以视作同一个状态
  2. 但是他加了个平方限制,即有继承关系,我们可以把每个平方数看成(A+B)^2.即我们对每个len可以记录他当前位符合条件的数的平方(ssum),与当前位符合条件的数的和(dsum)。
    1. 假设我们当前位的数字是i,如果我们已经记录了后代的B^2与B,后代一共有cnt个符合要求的数。
    2. 那么更新当前位数位dp。即(A+B)^2。数位dp。,即a+b。
#include <bits/stdc++.h>
using namespace std;
#define ll               long long
#define endl             "\n"
#define int              long long
const int N = 21;
const int mod=1e9+7;
struct node
{
	int cnt,dsum,ssum;
	node()
	{
		cnt=-1,dsum=0,ssum=0;//记录B^2和与B和
	}
	node(int a,int b,int c):cnt(a),dsum(b),ssum(c) {};
} dp[N][N][N];
int bit[N],pre[N];

node dfs(int len,int dsum,int ssum,bool limit)
{
	if(!len)return (dsum&&ssum?node(1,0,0):node(-1,0,0));
	if(!limit&&dp[len][dsum][ssum].cnt!=-1)return dp[len][dsum][ssum];
	node ans;
	int top=limit?bit[len]:9;
	for(int i=0; i<=top; ++i)if(i!=7)
			{
				node tmp=dfs(len-1,(dsum+i)%7,(ssum*10+i)%7,limit&&i==top);
				if(tmp.cnt!=-1)//子代有符合情况的
					{
						if(ans.cnt==-1)ans.cnt=0;
						int A=i*pre[len]%mod;
						ans.cnt=(ans.cnt+tmp.cnt)%mod;
						ans.ssum=(ans.ssum+tmp.ssum+2*tmp.dsum*A%mod+tmp.cnt*A%mod*A%mod)%mod;
						ans.dsum=(ans.dsum+tmp.dsum+tmp.cnt*A%mod)%mod;
					}
			}
	if(!limit)return dp[len][dsum][ssum]=ans;
	return ans;
}

int cal(int x)
{
	int k=0;
	while(x)bit[++k]=x%10,x/=10;
	return dfs(k,0,0,1).ssum;
}
void mysolve()
{
	int l,r;
	cin>>l>>r;
	cout<<(cal(r)-cal(l-1)+mod)%mod<<endl;
}

int32_t main()
{
	std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	ll t=1;
	cin >> t;
	pre[1]=1;
	for(int i=2; i<=20; ++i)pre[i]=pre[i-1]*10%mod;//预处理10^len
	while (t--)
		{
			mysolve();
		}
	system("pause");
	return 0;
}

例题2:Problem - 3709 (hdu.edu.cn)

思路:

  1. 数位只有18,而sum<=(9*19*18/2)<2000,我们可以枚举pos的每个位置来dp,如果最后sum=0,说明符合
  2. 如果sum<0,提前退出,不符合
  3. 所以他的状态是在当前枚举的是pos位,现在在len位时,累积sum的状态能获得的答案
  4. 需要考虑前导0,因为对于0,00,000000,哪个位置枚举pos都是一样的,重复计算len次,我们只需要1次。
#include <bits/stdc++.h>
using namespace std;
#define ll               long long
#define endl             "\n"
#define int              long long
const int N = 30;
int dp[N][N][2000],bit[N];

int dfs(int len,int sum,int pos,bool limit)
{
	if(!len)return sum==0;
	if(sum<0)return 0;
	if(!limit&&dp[len][pos][sum]!=-1)return dp[len][pos][sum];
	int ans=0;
	int top=limit?bit[len]:9;
	for(int i=0; i<=top; ++i)ans+=dfs(len-1,sum+i*(len-pos),pos,limit&&i==top);
	if(!limit)return dp[len][pos][sum]=ans;
	return ans;
}
int cal(int x)
{
	if(x<0)return 1;
	int k=0;
	while(x)bit[++k]=x%10,x/=10;
	int ans=0;
	for(int i=0; i<=k; ++i)ans+=dfs(k,0,i,1);//枚举pos
	return ans-k+1;//剔除多余前导0
}
void mysolve()
{
	int l,r;
	cin>>l>>r;
	cout<<cal(r)-cal(l-1)<<endl;
}

int32_t main()
{
	std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	ll t=1;
	cin >> t;
	memset(dp,-1,sizeof(dp));
	while (t--)
		{
			mysolve();
		}
	system("pause");
	return 0;
}

例题3:Problem - 4352 (hdu.edu.cn)

思路:

  1. 首先,我们知道在O(n*logn)下就可以求出lis。
  2. 因为数位的特性,lis<=10,数字是0~9,我们可以用状压来表示他的当前状态。用数字x存储状态,如果x的二进制上i位为1,说明其lis含这个数字,我们更新时也是更新x
  3. 这样,数位的状态就是在求取lis为k时,当前长度为len时,其当前lic的状态为x可能的答案数。
#include <bits/stdc++.h>
using namespace std;
#define ll               long long
#define endl             "\n"
#define int              long long
const int N = 20;
int dp[N][2000][N],bit[N];
int k;
int tocnt(int x)//计数lis
{
	int cnt=0;
	for(int i=0; i<10; ++i)if(x&(1<<i))cnt++;
	return cnt;
}

int update(int x,int p)
{
	for(int i=p; i<10; ++i)if(x&(1<<i))return (x^(1<<i))|(1<<p);//更新lis就是贪心从比p第一个大的数更新,表示lis为相同长度时,用p可以更小
	return x|(1<<p);//找不到比p大的,那么lis长度加1,即加了个p
}

int dfs(int len,int x,bool limit)
{
	if(!len)return tocnt(x)==k;
	if(!limit&&dp[len][x][k]!=-1)return dp[len][x][k];
	int ans=0;
	int top=limit?bit[len]:9;
	for(int i=0; i<=top; ++i)ans+=dfs(len-1,(!x&&!i?0:update(x,i)),limit&&i==top);//!x&&!i?0:update(x,i)处理第一次继承问题,即前面都是0,如果当前i为0或为其他的情况
	if(!limit)return dp[len][x][k]=ans;
	return ans;
}

int cal(int x)
{
	int len=0;
	while(x)bit[++len]=x%10,x/=10;
	return dfs(len,0,1);
}

int tt=0;
void mysolve()
{
	int l,r;
	cin>>l>>r>>k;
	cout<<"Case #"<<++tt<<": ";
	cout<<cal(r)-cal(l-1)<<endl;
}

int32_t main()
{
	std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	ll t=1;
	cin >> t;
	memset(dp,-1,sizeof(dp));
	while (t--)
		{
			mysolve();
		}
	system("pause");
	return 0;
}

到了这里,关于数位dp。的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • dp 就 dp ,数位dp是什么意思 ?

                                                                       💧 dp 就 dp ,数位dp是什么意思 ?💧           🌷 仰望天空,妳我亦是行人.✨ 🦄 个人主页——微风撞见云的博客🎐 🐳 数据结构与算法专栏的文章图文并茂🦕生动形

    2023年04月16日
    浏览(35)
  • 数位DP万能模板

    ☆* o(≧▽≦)o *☆嗨~我是小奥🍹 📄📄📄个人博客:小奥的博客 📄📄📄CSDN:个人CSDN 📙📙📙Github:传送门 📅📅📅面经分享(牛客主页):传送门 🍹文章作者技术和水平有限,如果文中出现错误,希望大家多多指正! 📜 如果觉得内容还不错,欢迎点赞收藏关注哟!

    2024年01月17日
    浏览(37)
  • 「学习笔记」数位 DP

    意义不大的题不写了。 点击查看目录 目录 「学习笔记」数位 DP 概述 例题 P2657 [SCOI2009] windy 数 思路 代码 P4317 花神的数论题 思路 P4124 [CQOI2016]手机号码 思路 代码 haha数 题意 思路 代码 0和1的熟练 题意 思路 代码 苍与红的试炼 题意 思路 代码 数位 DP 一般用来解决「在一个较

    2023年04月09日
    浏览(38)
  • 数位dp。

    在处理1e9甚至1e18,1e100的问题时,因为在统计情况下有很多重复的计算,数位dp实现了相同状态只计算一次,从而大幅减少运算时间,思想就是对每一位进行dp,计算时记忆化每一位可以有的状态。 如我们在统计1234的状态时,可以拆成统计0~10000,0~2000,0~300,0~40数位统计 我们

    2024年02月01日
    浏览(32)
  • NC200204 数位 DP

    题意 传送门 NC200204 dh的帽子 题解 可以独立考虑每一位是否合法。维护数字上下界的状态,枚举三个数字,仅对合法的状态进行转移。数位 DP 求解即可,时间复杂度 O ( l o g R ) O(logR) O ( l o g R ) 。

    2024年02月16日
    浏览(36)
  • 动态规划——数位DP 学习笔记

    引入 数位 DP 往往都是这样的题型:给定一个区间 ([l, r]) ,求这个区间中满足某种条件的数的总数。 简单的暴力代码如下: 而当数据规模过大,暴力枚举就 (mathbb T) 飞了,因此引入数位 DP: 概念 数位(digit):对于十进制,即把一个数字按照个位、十位、百位等,一位

    2024年02月08日
    浏览(44)
  • 周赛348(模拟、反向思维、数位DP)

    难度中等3 给你一个下标从 0 开始的字符串 s ,重复执行下述操作 任意 次: 在字符串中选出一个下标 i ,并使 c 为字符串下标 i 处的字符。并在 i 左侧 (如果有)和 右侧 (如果有)各 删除 一个距离 i 最近 的字符 c 。 请你通过执行上述操作任意次,使 s 的长度 最小化 。

    2024年02月07日
    浏览(72)
  • LeetCode---121双周赛---数位dp

    2996. 大于等于顺序前缀和的最小缺失整数 2997. 使数组异或和等于 K 的最少操作次数 2998. 使 X 和 Y 相等的最少操作次数 2999. 统计强大整数的数目 简单的模拟题,只要按照题目的要求去写代码即可,代码如下 这题考异或的性质---相同为0,相异为1,我们只要关心nums的异或和与

    2024年01月22日
    浏览(48)
  • 异或三角形(数位dp)

    [Link](异或三角 - 蓝桥云课 (lanqiao.cn)) 参考:2021蓝桥杯国赛-J异或三角形-数位dp_塔子哥来了的博客-CSDN博客_蓝桥杯数位dp 给定 T T T 个数 n 1 , n 2 , . . . , n T n_1,n_2,...,n_T n 1 ​ , n 2 ​ , . . . , n T ​ ,对每个 n i n_i n i ​ 请求出有多少组 a , b , c a,b,c a , b , c 满足: 1 ≤ a , b , c ≤

    2024年02月09日
    浏览(39)
  • 算法竞赛备赛进阶之数位DP训练

    数位DP的思想就是对每一位进行DP,计算时记忆化每一位可以有的状态,其作用是减少运算时间,避免重复计算。 数位DP是一种计数用的DP,一般就是要统计一个区间[A,B]内满足一些条件数的个数。 以1e9甚至1e18、1e100的问题为例,因为在统计情况下有很多重复的计算,数位DP实

    2024年01月16日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包