hdu(2089)——不要62(数位dp)

这道题很早之前就用数位dp做过一遍,但是那时候并没有理解透彻。今天又细细思考了一下,感觉比之前要好多了

题意:

就是叫你在[n,m]这个区间范围内找出不包含4和62的数字的个数。

思路:

1)暴力

2)数位dp:

做数位dp前,我们首先需要初始化,我们定义f[i][j]为开头为j的i位数中正常的数字有几个。

然后我们可以用三层for循环来对f数组进行初始化。

void init(){
	f[0][0]=1;<span style="white-space:pre">						</span>//别忘记了
	for(int i=1;i<=7;i++){
		for(int j=0;j<=9;j++){
			for(int k=0;k<=9;k++){
				if((j!=4)&&(!(j==6&&k==2))){<span style="white-space:pre">		</span>//这里的逻辑关系一开始我写成||(j!=6&&k!=2),这样子是有问题的
					f[i][j]=f[i-1][k]+f[i][j];
				}
			}
		}
	}
}

第一层我们for的是第几位,第二层我们则对第i位能够取到的数字进行枚举,第三层我们对第i-1位能够取到的数字进行枚举。因为当前第i位的取值关系只与第i-1位和当前第i位有关,所以我们就可以得到这个递推式了。

最重要的就是求数字有几个了。

今天我终于搞懂了为什么有时候是cal(m+1)-cal(n),而有时候是cal(m)-cal(n-1)了。

这个取决于你自己的dp是怎么定义的。当你定义为前i个数里面有多少个是符合条件的,那么就是我写的第一种写法。

如果定义为前i个数包括自己本身有多少个数是符合条件的,那么就是我写的第二种写法。

但是两种写法本质上并没有什么区别。

这道题我写的是第一种,即为不包含本身的。

求法为:

我们从低位到高位分别标记为1~n(n为位数),然后从高位开始寻找,然后后面代替的数字肯定不能大于当前那位的值,又因为我定义的是不包含自身,所以就得小于当前那位的值,然后ans+=已经求出来的f[i][j],就这样一位位的推下去,直至最后一位,所以这也就是我们最后一位为什么要加1的原因。

当然,当这位是4,或者这位是2并且前一位是6的情况下,那么直接跳出循环就好了。

int cal(int x){
	int tmp=x,t=0;
	while(tmp){
		dig[++t]=tmp%10;
		tmp=tmp/10;
	}
	dig[t+1]=0;
	int ans=0;
	for(int i=t;i>=1;i--){
		for(int j=0;j<dig[i];j++){
			if((j!=4)&&(!(j==2&&dig[i+1]==6)))
				ans+=f[i][j];
		}
		if((dig[i]==4)||(dig[i]==2&&dig[i+1]==6)) break;
	}
	return ans;
}

一开始我一直想不通为什么j循环到小于dig[i]就好了,其实就是定义的问题。

#include<stdio.h>
#include<string.h>
#include<queue>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<math.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 11
int dig[maxn];
int f[maxn][maxn];
void init(){
	f[0][0]=1;
	for(int i=1;i<=7;i++){
		for(int j=0;j<=9;j++){
			for(int k=0;k<=9;k++){
				if((j!=4)&&(!(j==6&&k==2))){
					f[i][j]=f[i-1][k]+f[i][j];
				}
			}
		}
	}
}
int cal(int x){
	int tmp=x,t=0;
	while(tmp){
		dig[++t]=tmp%10;
		tmp=tmp/10;
	}
	dig[t+1]=0;
	int ans=0;
	for(int i=t;i>=1;i--){
		for(int j=0;j<dig[i];j++){
			if((j!=4)&&(!(j==2&&dig[i+1]==6)))
				ans+=f[i][j];
		}
		if((dig[i]==4)||(dig[i]==2&&dig[i+1]==6)) break;
	}
	return ans;
}
int main(){
	int n,m;
	init();
	while(~scanf("%d%d",&n,&m)){
		if(n==0&&m==0) break;
		printf("%d\n",cal(m+1)-cal(n));<span style="white-space:pre">		</span>//!!!
	}
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-12-25 20:08:47

hdu(2089)——不要62(数位dp)的相关文章

[ACM] hdu 2089 不要62(数位Dp)

不要62 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 19043    Accepted Submission(s): 6442 Problem Description 杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer). 杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就

HDU 2089 不要62(数位DP,三种姿势)

HDU 2089 不要62(数位DP,三种姿势) ACM 题目地址:HDU 2089 题意: 中文题意,不解释. 分析: 100w的数据,暴力打表能过 先初始化dp数组,表示前i位的三种情况,再进行推算 直接dfs,一遍搜一变记录,可能有不饥渴的全部算和饥渴的部分算情况,记录只能记录全部算(推荐看∑大的详细题解Orz) 代码: 1. 暴力 (以前写的) /* * Author: illuz <iilluzen[at]gmail.com> * File: 2089_bf.cpp * Create

HDU 2089 不要62(数位dp)

题意:给定一个区间[n, m],求这个区间当中不含62和4的个数.例如62315,88914就是不符合条件的. 数位dp第一题,dp[i][j]表示以 j 开头的 i 位数满足条件的个数,先要预处理出来所有的情况, 下面是预处理的核心.其中k表示j后面的那一位.max_len是题目中给的n的最大位数,这里是7,第二层for是枚举第max_len - i位的值. for (int i = 1; i <= max_len; i++) { for (int j = 0; j <= 9; j++) {

HDU 2089 不要62(数位dp&amp;记忆化搜索)

题目链接:[kuangbin带你飞]专题十五 数位DP C - 不要62 题意 杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer). 杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍,更安全地服务大众. 不吉利的数字为所有含有4或62的号码.例如: 62315 73418 88914 都属于不吉利号码.但是,61152虽然含有6和2,但不是62连号,所以不属于不吉利数字之列. 你的任务是,对于每次给

HDU 2089 不要62 (数位dp水题)

不要62 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 32358    Accepted Submission(s): 11514 题目链接 Problem Description 杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer).杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样

HDU 2089 不要62 数位DP入门

Problem Description 杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer).杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍,更安全地服务大众.不吉利的数字为所有含有4或62的号码.例如:62315 73418 88914都属于不吉利号码.但是,61152虽然含有6和2,但不是62连号,所以不属于不吉利数字之列.你的任务是,对于每次给出的一个牌照区间号,推断出交管局今次又要实际上给多

HDU 2089 不要62 数位dp(入门

题目链接:点击打开链接 中文题. 问区间内有多少个合法数字(一个数是合法数字须满足1.不含4 ,2.每个6后面都不是2 dp[i][0]表示长度为i的数字中,前一位不为6时的合法个数, dp[i][1]表示长度为i的数字中前一位是6的合法个数. #include <cstdio> #include <algorithm> #include <cstring> #include <iostream> using namespace std; typedef l

HDU 2089 不要62 (数位DP)

Problem Description 杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer).杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍,更安全地服务大众.不吉利的数字为所有含有4或62的号码.例如:62315 73418 88914都属于不吉利号码.但是,61152虽然含有6和2,但不是62连号,所以不属于不吉利数字之列.你的任务是,对于每次给出的一个牌照区间号,推断出交管局今次又要实际上给多

hud 2089 不要62 (数位dp)

#include<stdio.h> #include<string.h> #include<math.h> #define max 10 int dp[max][3]; int number[max]; //dp[i][0] 前i位数中不符合要求的总个数 //dp[i][1] 前i位数中最高位是2的个数 //dp[i][2] 前i位数中存在含4和有连续62的个数 void init() { memset(dp,0,sizeof(dp)); dp[0][0]=1; for

HDU 3555 Bomb ,HDU 2089 深刻学习数位dp (各种方法乱用)

数位dp是与数位有关的区间统计 参考: 点击打开链接 思想是将数字拆分,dp[i][j] 长度为i的数字有多少个满足j条件的,从000~999 统计时,计当前数字为x,则其后面数字的变化的倍数是0~x-1 后面接000~999这样刚好统计不到这个数字n本身. eg: 对于一个数,若其首位已经比询问上界小,则剩余位没有任何限制.此时如果能直接处理这一情况,则问题距离解决又会迈出一大步. 例如,在十进制下,计算[10000,54321]内的数字和,我们可以将其分解为: [10000,19999],[