dp动态规划

数位dp

离谱dp,常用于有位置与位置之间的限制并计数的问题中。通过记忆化搜索求出。

代码大致模板:

const int N = 50;
//数的最高位数,可以往大点开

int s[N], tot;
//栈用来存每位的数值
int dp[N][2][2];
//状态可能还会多一些,大致与 Dfs 状态同步

inline int Dfs(int x, bool f1, bool f2){
//x 表示当前是第几位,f1 表示是否为前缀0部分,f2 表示是否为连续最高位
//后面会依题目加其他限制,视情况而定
	if(){
	//有的题目此处会有无法产生贡献的性质
		return 0;
	}
	if(!x){
	//枚举完了
		return 1;
		//此处看题目要求返回贡献值
	}
	
	if(dp[x][f1][f2] != -1){
	//记忆化
		return dp[x][f1][f2];
	}
	
	int mx = f2 ? s[x] : 9;
	//看当前位的最大取值范围,因题目而异,如十进制为 9 ,二进制为 1
	int sum = 0;
	//统计当前答案用
	
	for(int i = 0; i <= mx; ++ i){
		sum += Dfs(x - 1, f1 && (i == 0), f2 && (i == mx));
		//转移,继承状态
	}
	
	return dp[x][f1][f2] = sum;
	//记忆化
}

inline int ask(int x){
	tot= 0;
	//清空栈
	while(x){
		s[++ tot] = x % 10;
		//取最后一位
		x /= 1;
		//除去最后一位
	}
	memset(dp, -1, sizeof(dp));
	//清空 dp 数组
	return Dfs(tot, 1, 1);
	//返回答案
	//起始状态肯定满足为最大位且为前缀0位
}

例题:

P4124 [CQOI2016] 手机号码

发现题目中的限制为:无前缀0, 3 个相邻的相同数字,不同时出现 4 ,8 ,一定为 11 位。

那我们就要在状态中体现出这些限制,令 dp[x][l][ll][x4][x8][y][f1][f2] 表示:当前为第 x 位,上一位数值l ,上上位数值ll ,是否有 4 ,是否有 8 ,是否满足有三个连续相同,是否为前缀0部分,是否为连续最高位。

同理,Dfs 也为这些状态。在有不满足限制时返回 0 即可,其余为正常转移。注意位数的特判,不满 11 位时也需直接返回 0

P4317 花神的数论题

关于 sumi 明显为数位dp求的东西,但最后的乘积的不可能直接计算的,我们需要转换思路。

数位dp是用来求满足条件的数量的,我们便可以将题目转为求出每个 sumi=xi 的个数,答案用快速幂计算即可,复杂度不高。

posted @   Biuld  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示