首页 > 其他分享 >数位DP的一般方法

数位DP的一般方法

时间:2024-02-06 13:11:39浏览次数:41  
标签:int zero DFS windy len 方法 DP 数位

数位DP?数位DFS!

P2657 [SCOI2009] windy 数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

不含前导零且相邻两个数字之差至少为 2 的正整数被称为 windy 数。windy 想知道,在 a 和 b 之间,包括 a 和 b ,总共有多少个 windy 数?

我们使用DFS解决。数位DFS要设计好状态,考虑好哪些条件会对当前填位造成影响,将其作为参数传递下去。填位,即将该位填上数,并将当前状态DFS下去。回溯的时候各个分支统计好答案,问题解决。

当然,肯定会T。所以我们需要将搜索到的状态记忆化,这样就和DP没有区别了,因为实质上都是避免了重复做事情,该做的一点都没少做。

称之大名鼎鼎的记忆化搜索。

下面,我们通过代码来解释细节:

#include<bits/stdc++.h>
#define int long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define rep2(i,a,b) for(int i=a;i<b;i++)
using namespace std;
const int INF=0x7fffffff;

int num[15],len,f[15][15][2][2]; //我们需要放入的参数是len当前位置、last上一位填了谁、flag先前的所有位是否紧贴上界、zero上一位是否是前导零
int dfs(int len,int last,bool flag,bool zero){
	if(!len)return 1; //搜完了,ans++;
	if(~f[len][last][flag][zero])return f[len][last][flag][zero]; //之前搜过,直接返回;
	int res=0;
	rep(i,0,9){ //从0到9每一位试;
		if((i<=num[len]||!flag)&&(abs(i-last)>=2||zero)){
            //如果先前高位已经紧贴上界,则该位不能超过当前位的上界,否则该数会超过上界;其他情况都是合法的
            //windy数的要求是与上一位相差至少2;如果上一位是前导零,则不用管
			res+=dfs(len-1,i,flag&&(i==num[len]),zero&&(!i)); 
            //递归,位置-1,填的数是i,如果先前flag就为1并且该位也紧贴上界,那么flag继续为1,否则为0;如果上一位就是前导零这一位也是0,那么zero继续为0
		}
	}
	f[len][last][flag][zero]=res; //记录并返回
	return res;
}

int solve(int x){
	int tmp=x;
	len=0;
	memset(num,0,sizeof(num));
	while(tmp){
		num[++len]=tmp%10;
		tmp/=10;
	} //先把上界拆了位,放数组里方便查询
	memset(f,-1,sizeof(f)); //dp数组f在处理不同的答案时需要重置
	return dfs(len,11,1,1); //最初的last传递为11,以保证第一位填9时是合法的
}

signed main(){
	std::ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	int a,b;
	cin>>a>>b;
	cout<<solve(b)-solve(a-1)<<endl; //[a,b]的windy数,等于[1,b]的减去[1,a-1]的;
    return 0;
}

您会发现,其实状态除了那四个参数以外没有记录别的东西,包括我们现在填上的数(所有位)到底是什么。为什么不用记录呢?

其实,那并不能决定我们下一位可以填什么不可以填什么,属于无用信息。真正有用的,我们已经用这四个参数表示出来了。很巧妙吧?

并且,您要真是把状态表示成那样了,就没有什么可记忆化的了,因为每个状态都是unique的。而只用这四个参数,我们可以搜索到许多完全相同的情况,这时候记忆化就非常有用了。您不妨设想,搜索是一棵树,状态是节点,而len就是深度。在同一个深度上,有很多father不同的节点;而last、flag和zero在这些节点上往往会存在许多相同的,所以状态就在此重复了,我们无需再搜索一次,他们必然和先前搜到的该状态下的答案是一样的。

总结一下数位DFS:

  1. 拆位;
  2. 设计状态,越简越好,但一定要把必要信息表示全;
  3. DFS时,根据参数中的信息,进行条件筛选并填上不同的位搜索下去;
  4. 统计答案,记录并返回。
  5. 别忘了记忆化。

就OK了。值得注意的是,每一种DP都是灵活多变的,读者需要扩展思维,切莫拘泥于模版。

FIN.

标签:int,zero,DFS,windy,len,方法,DP,数位
From: https://www.cnblogs.com/DaisySunchaser/p/18009540

相关文章

  • Nginx配置TCP/UDP流量转发
    #usernobody;worker_processes1;#error_loglogs/error.log;#error_loglogs/error.lognotice;#error_loglogs/error.loginfo;#pidlogs/nginx.pid;events{worker_connections1024;}stream{log_formatmain'$remote_addr[$tim......
  • power shell 查询版本 方法大全
    在PowerShell中查看版本信息可以通过多种方式实现。以下是几种常用的方法来查看你当前使用的PowerShell版本:方法1: $PSVersionTable 变量这是检查PowerShell版本最简单和最常用的方法。只需在PowerShell窗口中输入以下命令:powershellCopyCode$PSVersionTable.PSVersion此......
  • JAVA对象的成员方法
    方法介绍使用方法的好处......
  • leetcode--5. 最长回文子串(dp)
    记录23:292024-2-5https://leetcode.cn/problems/longest-palindromic-substring/dp[i][j]s[i,j]之间能否构成回文子串[i,j]之间是否能够构成需要考虑[i+1,j-1]是否构成回文子串且s[i]==s[j]当j-1>=i+1时候说明正好是俩个相邻的字符,此时如果s[i]==s[j]就肯定可......
  • 方法的重载和命令行传参
    方法的重载重载就是在一个类中,有相同的函数名称,但形参不同的函数。方法的重载的规则:方法名称必须相同。参数列表必须不同。(个数不同、或类型不同,参数排列顺序不同等)方法的返回类型可以相同也可以不相同。仅仅返回类型不同不足以成为方法的重载。实现理论:方法名称相......
  • 锁优化的方法
    减少锁持有时间减少锁粒度将大对象拆分成小对象,增加并行度,降低锁竞争。ConcurrentHashMap允许多个线程同 时进入锁分离根据功能进行锁分离ReadWriteLock在读多写少时,可以提高性能。锁消除锁消除是发生在编译器级别的一种锁优化方式。有时候我们写的代码完全不需要加......
  • ReentrantLock源码分析、LockSuppor、ReentrantReadWriteLock、锁优化的方法
    ReentrantLock类图我们看一下重入锁ReentrantLock类关系图,它是实现了Lock接口的类。NonfairSync和FairSync都继承自抽象类Sync,在ReentrantLock中有非公平锁NonfairSync和公平锁FairSync的实现。在重入锁ReentrantLock类关系图中,我们可以看到NonfairSync和FairSync都继承自抽象......
  • ReentrantLock源码分析、LockSuppor、ReentrantReadWriteLock、锁优化的方法
    ReentrantLock类图我们看一下重入锁ReentrantLock类关系图,它是实现了Lock接口的类。NonfairSync和FairSync都继承自抽象类Sync,在ReentrantLock中有非公平锁NonfairSync和公平锁FairSync的实现。在重入锁ReentrantLock类关系图中,我们可以看到NonfairSync和FairSync都继承自抽象......
  • kubelet 组件内存高排查方法
    1、查看服务进程,并跟踪程序系统调用pgrep kubelet#查看资源占用情况top-p 95786strace-cp95786#显示时间戳strace-tt-p95786 2、用pprof性能分析工具排查#安装go环境#启动代理kubectlproxy--port=8001--address=0.0.0.0curl-sK-vhttp://127.0.0.1:8001/......
  • 什么是Vue样式穿透以及常用的实现方法
    在Web前端开发中,样式穿透是一个重要的主题,它可以帮助我们更好地定制化组件样式,提升用户体验。本文将为您介绍Vue中样式穿透的概念,以及几种常用的实现方法,希望对您的前端开发工作有所帮助。什么是样式穿透?样式穿透是一种在Vue组件中使用父组件的样式来渲染子组件的技术。在Vue中,子组......