poj3208 Apocalypse Someday[数位DP]
数位中出现至少3个连续的'6'的数字(称魔鬼数),询问满足要求的排名k的数。
经典题型。采用试填法。
递推做法:预处理出$i$位数字中满足要求的数(下记为'魔鬼数')。对每一位都从0到9试一遍,然而卡在了试填时试到6这个数时该怎么办,不太会做。然后才知道可以记录填到目前的上一位已有多少个连续的6,这样可以配合当前的计算出来。
所以就有这个$f[i][0]=9*(f[i-1][0]+f[i-1][1]+f[i-1][2]),f[i][1]=f[i-1][0],f[i][2]=f[i-1][1],f[i][3]=10*f[i-1][3]+f[i-1][2]$
其中0表示开头有0个6,1表示开头有一个6,2同理,3表示当前小于等于i位的魔鬼数的数量(也就相当于把各位数的魔鬼数累积起来)
注意是算入前导0的。比如有004566674,因为试填时他也算一种。而开头不必担心,看代码第1次循环就会发现考虑0的时候恰好把小于i位的魔鬼数数量都减掉了,说不太清楚,还是自己想想吧。
用k以辅助记录当前填过的末尾连续的6数量,用于处理填6时计算,每填一个6就多算一下。k到3时表示我已经诞生连续3个的6了,所以后面所有的填法都合法,这是要再单独特殊处理,看code。
其他就是常规的试填。
说两点细节:
注意边界处理 ,考虑预处理DP值的边界,尤其是f[0],f[1]等;
试填也要考虑边界。比如开始时。结束时(最后一位),这直接和f[0][0]的初始化产生了联系。所以两个边界都要看一下检查正确性。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define dbg(x) cerr<<#x<<" = "<<x<<endl
#define ddbg(x,y) cerr<<#x<<" = "<<x<<" "<<#y<<" = "<<y<<endl
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
template<typename T>inline char MIN(T&A,T B){return A>B?A=B,:;}
template<typename T>inline char MAX(T&A,T B){return A<B?A=B,:;}
template<typename T>inline T _min(T A,T B){return A<B?A:B;}
template<typename T>inline T _max(T A,T B){return A>B?A:B;}
template<typename T>inline T read(T&x){
x=;int f=;char c;while(!isdigit(c=getchar()))if(c=='-')f=;
while(isdigit(c))x=x*+(c&),c=getchar();return f?x=-x:x;
}
ll f[][],rk,cnt;
int T;
inline void preprocess(){
f[][]=,f[][]=;f[][]=;//←注意边界处理 ,DP和试填都要检验一下
for(register int i=;i<=;++i)f[i][]=*(f[i-][]+f[i-][]+f[i-][]),f[i][]=f[i-][],f[i][]=f[i-][],f[i][]=*f[i-][]+f[i-][];
} int main(){//freopen("test.in","r",stdin);freopen("test.out","w",stdout);
read(T);preprocess();while(T--){
read(rk);int x;for(x=;x<=;++x)if(rk<=f[x][])break;
for(register int i=x,k=;i;--i){
for(register int j=;j<=;++j){
cnt=f[i-][];
if(k==)cnt+=f[i-][]+f[i-][]+f[i-][];
else if(j==)for(register int l=-k;l<;++l)cnt+=f[i-][l];
if(rk<=cnt){
if(k<){if(j==)++k;else k=;}
printf("%d",j);break;
}
else rk-=cnt;
}
}
puts("");
}
return ;
}
然后讲一下记搜的,可以套模板。求第k大的数,可以转化为二分枚举区间[1,n]的右边界n,看[1,n]的满足要求的数,由于其随区间变大而单调增,所以是二分。通过不断调整,可以找到最小n使得区间[1,n]恰有k个满足要求的数。这个因为是套模板,所以很好写。但是二分的话会让复杂度多一个log。。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define dbg(x) cerr<<#x<<" = "<<x<<endl
using namespace std;
typedef long long ll;
template<typename T>inline char MIN(T&A,T B){return A>B?A=B,:;}
template<typename T>inline char MAX(T&A,T B){return A<B?A=B,:;}
template<typename T>inline T _min(T A,T B){return A<B?A:B;}
template<typename T>inline T _max(T A,T B){return A>B?A:B;}
template<typename T>inline T read(T&x){
x=;int f=;char c;while(!isdigit(c=getchar()))if(c=='-')f=;
while(isdigit(c))x=x*+(c&),c=getchar();return f?x=-x:x;
}
int T;
ll L,R,f[][][][],b[],aim;
ll dp(int len,bool las,bool las2,bool fil,bool limit){
if(!len)return fil;
if(!limit&&~f[len][las][las2][fil])return f[len][las][las2][fil];
ll cnt=;int num=limit?b[len]:;
for(register int i=;i<=num;++i)cnt+=dp(len-,i==,las,fil||(las&&las2&&i==),limit&&i==num);
return limit?cnt:f[len][las][las2][fil]=cnt;
}
inline ll solve(ll x){
int k=;while(x)b[++k]=x%,x/=;
return dp(k,,,,);
} int main(){//freopen("tmp.txt","r",stdin);//freopen("test.out","w",stdout)£»
memset(f,-,sizeof f);
read(T);while(T--){
read(aim);L=,R=1e10;ll mid;
while(L<R){
mid=L+R>>;
if(solve(mid)<aim)L=mid+;
else R=mid;
}
printf("%lld\n",L);
}
return ;
}
poj3208 Apocalypse Someday[数位DP]的更多相关文章
- poj3208 Apocalypse Someday 数位dp+二分 求第K(K <= 5*107)个有连续3个6的数。
/** 题目:poj3208 Apocalypse Someday 链接:http://poj.org/problem?id=3208 题意:求第K(K <= 5*107)个有连续3个6的数. ...
- POJ 3689 Apocalypse Someday [数位DP]
Apocalypse Someday Time Limit: 1000MS Memory Limit: 131072K Total Submissions: 1807 Accepted: 87 ...
- POJ3208 Apocalypse Someday(二分 数位DP)
数位DP加二分 //数位dp,dfs记忆化搜索 #include<iostream> #include<cstdio> #include<cstring> usin ...
- POJ3208 Apocalypse Someday
题意 Language:Default Apocalypse Someday Time Limit: 1000MS Memory Limit: 131072K Total Submissions: 2 ...
- POJ-3208 Apocalypse Someday (数位DP)
只要某数字的十进制表示中有三个6相邻,则该数字为魔鬼数,求第X小的魔鬼数\(X\le 5e7\) 这一类题目可以先用DP进行预处理,再基于拼凑思想,用"试填法"求出最终的答案 \( ...
- POJ 3208-Apocalypse Someday(数位dp)
题意:给定n,输出第n大包含666的数字. 分析:dp[i][j][k][l]表示 长度为i,当前位是否是6,前一位是否6,是否已经包含666,表示的数量,再用二分找出第n大的这样的数字. #incl ...
- 数位DP专题
这周开始刷数位DP,在网上找到一份神级数位DP模板,做起题目来爽歪歪. http://www.cnblogs.com/jffifa/archive/2012/08/17/2644847.html in ...
- 开坑数位dp
[背景] 在10月3日的dp专练中,压轴的第6题是一道数位dp,于是各种懵逼. 为了填上这个留存已久的坑,蒟蒻chty只能开坑数位dp了. [例题一][HDU2089]不要62 题目大意:给你一个区间 ...
- 数位dp真·浅谈 By cellur925
预警:由于是从$Vergil$学长那里和$Mathison$大神那里学来的,所以清一色记忆化搜索!qwq 巨佬的数位dp讲解(未来的咕咕日报头条): https://www.luogu.org/blo ...
随机推荐
- MATLAB实现OTSU
目录 1.OTSU算法原理简述: 2.MATLAB实现代码 @ 1.OTSU算法原理简述: 最大类间方差是由日本学者大津(Nobuyuki Otsu)于1979年提出,是一种自适应的阈值确定方法.算法 ...
- @Value注解
1.注入 基本字符 public class Student { @Value("qq") private String name; @Value("12") ...
- 【Python开发】Python:itertools模块
Python:itertools模块 itertools模块包含创建有效迭代器的函数,可以用各种方式对数据进行循环操作,此模块中的所有函数返回的迭代器都可以与for循环语句以及其他包含迭代器(如生成器 ...
- 【python基础学习】---解析多层json,解析xml
1.以豆瓣的API接口为例子,解析返回的json数据 https://api.douban.com/v2/book/1220502 { "rating":{ "max&q ...
- filter方法常用过滤条件
#encoding: utf-8 from sqlalchemy import create_engine,Column,Integer,String,Float,func,and_,or_ from ...
- PowerShell学习记录
一.简介——连接 Powershell 是运行在windows机器上实现系统和应用程序管理自动化的命令行脚本环境.你可以把它看成是命令行提示符cmd.exe的扩充,不对,应当是颠覆. powershe ...
- Oracle 获取当前日期是月的第几周
函数FUNC_GET_WEEKOFMONTH: IW 是年的自然周: WW是年的第一天起开始算7天为一周 FUNCTION FUNC_GET_WEEKOFMONTH (V_PSD DATE) RETU ...
- zookeeper知识
zookeeper是一个管理的作用 zookeeper有一个老大叫:leader.跟着老大的有两个小弟follwer,follwer 叫做跟随者 连接zookeeper的六个节点我们称它为客户端 zo ...
- PHP 时间转几分几秒
public static function timetodate($c){ if($c < 86400){ $time = explode(' ',gmstrftime('%H %M %S', ...
- linux小白家教学<一>
<数据中心规划与实施> 教学大纲 编写人:Allen 一. 课程教学内容及目标: (一) 知识目标 1.掌握企业级LINUX部署以及相关配置: 2.掌握LINUX操作系统基本的创建.删除 ...