dp东西实在太多,昨天开了个树形dp入门,还没入呢,今天就要写数位dp,也不知道这种学习状态对不对啊?

A - 不要62

题意:

输入n到m内,符合条件的数的个数。条件:不含62和4。

这里直接学习qkoqhh大佬的思路。

 #include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cmath>
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,l,r) for(int i=l;i>=r;i--)
#define link(x) for(edge *j=h[x];j;j=j->next)
#define eps 1e-8
#define inf 1000000007
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define succ(x) (1<<x)
#define lowbit(x) (x&(-x))
#define sqr(x) ((x)*(x))
#define mid (x+y>>1)
#define NM 305
#define nm 1000005
#define pi 3.141592653
using namespace std;
int read(){
int x=,f=;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-;ch=getchar();}
while(isdigit(ch))x=x*+ch-'',ch=getchar();
return f*x;
}
int d[][],_x,n,b[];
//f:高位与原位是否相同
int dfs(int n,bool f,int t){
if(!n)return ;
if(!f&&d[n][t])return d[n][t];
int ans=,m=f?b[n]:;
for(int i=;i<=m;i++)
if(i!=&&!(t==&&i==)){
ans+=dfs(n-,f&&i==m,i);
//printf("%d %d:%d\n",n,i,ans);
}
return d[n][t]=ans;
}
int solve(int x){
mem(b);mem(d);
for(n=;x;x/=)b[++n]=x%;
return dfs(n,true,);
}
int main(){
while(_x=read())printf("%d\n",solve(read())-solve(_x-));
return ;
}

下面贴上我学习这份优秀代码的一点注释

 #include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int bit[], d[][];
int dfs(int pos, bool flag, int t){
/*
* pos:当前位的位置;
* flag:高位与原位相同(true)
* t:高位,即当前位的前一位
*/
if(!pos) return ;
if(!flag&&d[pos][t]) return d[pos][t];
//高位与原位不同, 直接返回,不用考虑当前位与原位大小
int ans = , m = flag?bit[pos]:;
for(int i=;i<=m;i++){
if(i!= && !(i==&&t==)){
ans+=dfs(pos-, flag&&i==m, i);
//这里第二个参数可依据flag的定义理解
//同样这里比较灵活,有时需要分类讨论
}
}
return d[pos][t]=ans;
}
int solve(int x){
memset(bit, , sizeof(bit));
memset(d, , sizeof(d));
int len = ;
while(x){
bit[++len] = x%;
x /= ;
}
return dfs(len, true, );
}
int main(){
int n, m;
while( scanf("%d%d", &n, &m)!=EOF && n && m){
printf("%d\n", solve(m)-solve(n-));
}
return ;
}

B - B-number

题意: 统计1到n中有13且能被13整除的数。

这是我写的第二道数位dp题,目前对我来说,数位dp题的难点在于状态的转移合理正确的统计(这个比较灵活)。

拿到这道题,思考后对比上题的模板,需要新增参数判断高位前是否出现了13,以及如何处理能被13整除。这里余数的处理用到了 同余定理乘法以及加法的运用,这个技巧我不熟练(不知道),这里余数的参数传递就有难度了。

 #include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int bit[], d[][][][], c[];
bool vis[][][][];
int dfs(int pos, bool flag, bool is13, int t, int mod){
/*
* pos:当前位的位置;
* flag:高位与原位相同(true)
* is13:高位前是否出现13
* t:高位,即当前位的前一位
* mod:当前数/13的余数
*/
if(!pos){
if(is13 && !mod) return ; //出现13,且mod为0,可以整除
else return ;
}
if(!flag&&vis[pos][t][mod][is13]) return d[pos][t][mod][is13];
int ans = , m = flag?bit[pos]:;
for(int i=;i<=m;i++){
if((t== && i==) || is13){
/*
*int chushu = (mod*10 + i)%13;
*这里不能这样算,有种自己在胡搞的感觉
*
* 这里是同余定理加法和乘法的应用,其实不用深究,写个例子就明白了
* 可以这样理解 :
* x%13=(c[i]*10^i + c[j]*10^j + ……)%13 = (mod + c[j]*10^j + ……)%13
* 拆开后就好
* 这里可以预处理c[]
* 传递时仔细对待mod
*/
int chushu = (mod + c[pos]*i)%;
ans += dfs(pos-, flag&&i==m, true, i, chushu);
}else{
int chushu = (mod + c[pos]*i)%;
ans += dfs(pos-, flag&&i==m, false, i, chushu);
}
}
vis[pos][t][mod][is13]=true;
return d[pos][t][mod][is13]=ans;
}
int solve(int x){
memset(bit, , sizeof(bit));
memset(d, , sizeof(d));
memset(vis, , sizeof(vis));
int len = ;
while(x){
bit[++len] = x%;
x /= ;
}
return dfs(len, true, false, , );
}
int main(){
int n;
c[] = ;
for(int i=; i<=; i++) c[i]=c[i-]*%;
while( scanf("%d", &n)!=EOF && n ){
printf("%d\n", solve(n));
}
return ;
}

C - Balance Number

题意:

定义一种平衡数:在pivot两边的数与pivot的torques之和相等,统计[x,y]内bn的个数。

这里记录下做这题的思路(虽然这题写出来没过样例,但感觉自己思路还说的过去):

先思考状态转移所需要的参数,即维度:除前面模板里套路的pos和flag外,还需要pivot的位置,还需要计算和比较pivot两边的torques之和是否相等。

前三个维度各设一个参数,最后的比较可以设置为一个torqueses,对左右扭矩求向量和,若符合条件,则最终和为0(这里我没想到)。

下来就是上面那个模板的套路去写。有几个需要注意的细节(其实都是我开始没想到的,对大佬来说这些很显然的东西,对我就从没显然过):

  1. 若扭矩和<0,直接返回,这里相当于利用一个剪枝,难思考但是不难理解。
  2. 就是在0的情况下,这种数无论pivot在哪都符合条件所以多级算了(len-1)次。dfs()搜索是0的情况是len位全0。
  3. 就是这道题有个记忆化搜索的如何记忆化的问题。什么时候更新dp,时间上还是有差距的,下面的代码在这点上与上面的模板不同。
 #include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#define LL long long
const int INF = 0x3f3f3f3f;
LL dp[][][];
int bit[];
/*
* pos:当前位位置
* pivot: 题中所描述的balanced number的"中间"的那个点
* torques:这里判断pivot左右扭矩之和是否相等可以用求其和torques看是否等于0
* 这里最开始我的想法是用两个参数,扫描到pos=0时判断其是否相等,这样就烦琐了
* flag:同理前面的模板
*/
LL dfs(int pos, int pivot, int torques, bool flag){
if(!pos) return torques==;
//若枚举结束,扭矩和为0,则说明pivot这点可以使这个数成为balance number
if(torques < ) return ;
//这里如果没有搜索完,扭矩和就小于0,则这个数一定不会是b n,这个判断很巧妙,这个有了方向后计算很方便
if(!flag && dp[pos][pivot][torques]!=-) return dp[pos][pivot][torques];
LL ans = ;
LL m = flag?bit[pos]:;
for(int i=; i<=m; i++){
ans += dfs(pos-, pivot, torques+i*(pos-pivot), flag&&i==m);
}
if(!flag) dp[pos][pivot][torques]=ans;
/*
* 1、这里相对前面的模板不同。 这里记忆化对我来说又得注释一下,
* 这里高位与低位相同时,
* 当前pos是不能记忆的因为这里只枚举了0-bit[i]位,不符合总的dp[][][]状态的定义
* pos位,pivot点,扭矩和为torques的所有数 的个数
* 2、同样也可以按前面的模板来写,不过需要对每个数的dp进行memset()。
*
* 对比下二者的时间,156 : 249
*/
return ans;
}
LL solve(LL x){
if(x < ) return ;
memset(bit, , sizeof(bit));
int len = ;
while(x){
bit[++len] = x%;
x /= ;
}
LL ans = ;
for(int i=; i<=len; i++){
ans += dfs(len, i, , true);
}
return ans-(len-);
/*
* 这里结果减去(len-1)我又没想到,在纸上画了画,
* 发现在dfs过程中,重复计算了一种为0的情况,
* 这个情况要筛去(len-1)个
*/
}
int main(){
int T;
scanf("%d", &T);
memset(dp, -, sizeof(dp));
while(T--){
LL x, y;
scanf("%lld%lld", &x, &y);
printf("%lld\n", solve(y)-solve(x-));
}
}

还有道C,改天补。

这里想到了dp第一天韦神对A题有一种不同于上面那种解法的解法,因为代码看着比较复杂,一直没有仔细看,只是粗略的对比了二份代码的不同,发现那个思路比较注重对各种情况的全面讨论,难点在于对状态的抽象以及转移过程中的分类讨论,但是dp的维度是优于这个的,情况的表示也相对直观。这里先放上代码,改日细看。

 #include <stdio.h>
#include <string.h>
#include <string.h>
#include <iostream>
using namespace std;
int dp[][];//dp[i][j],i代表数字的位数,j代表状况
//dp[i][0],表示不存在不吉利数字
//dp[i][1],表示不存在不吉利数字,且最高位为2
//dp[i][2],表示存在不吉利数字
void Init()
{
memset(dp,,sizeof(dp));
int i;
dp[][] = ;
for(i = ; i<=; i++)//数字最长为6
{
dp[i][] = dp[i-][]*-dp[i-][];//最高位加上不含4的9个数字的状况,但因为会放6,所以要减去前一种开头为2的情况
dp[i][] = dp[i-][];//开头只放了2
dp[i][] = dp[i-][]*+dp[i-][]+dp[i-][];//已经含有的前面放什么数都可以,或者是放一个4,或者是在2前面放6
}
} int solve(int n)
{
int i,len = ,tem = n,ans,flag,a[];
while(n)//将每一位拆分放入数组
{
a[++len] = n%;
n/=;
}
a[len+] = ans = ;
flag = ;
int bb=;
for(i=len; i>=; i--)
{
ans+=dp[i-][]*a[i];
if(!flag && a[i]>)//首位大于4,可以有放4的情况
ans+=dp[i-][];
if(!flag && a[i+]== && a[i]>)//后一位为6,此位大于2
ans+=dp[i][];
if(!flag && a[i]>)//此位大于6,可能的62状况
ans+=dp[i-][];
if(a[i]== || (a[i+]==&&a[i]==)){
bb=;
for(int j=i-;j>=;j--){
bb=bb*+a[j];
}
break;
}
}
return tem-ans-bb;
} int main()
{
int l,r;
Init();
while(~scanf("%d%d",&l,&r),l+r)
{
// solve(4555);
printf("%d\n",solve(r+)-solve(l));
//因为solve函数中并没有考虑n是不是不幸数的情况,所以r+1只算了1~r,而l只算了1~l-1,这两者相减才是正确答案
} return ;
}

数位dp作业的更多相关文章

  1. uva12063数位dp

    辣鸡军训毁我青春!!! 因为在军训,导致很长时间都只能看书yy题目,而不能溜到机房鏼题 于是在猫大的帮助下我发现这道习题是数位dp 然后想起之前讲dp的时候一直在补作业所以没怎么写,然后就试了试 果然 ...

  2. 数位dp踩坑

    前言 数位DP是什么?以前总觉得这个概念很高大上,最近闲的没事,学了一下发现确实挺神奇的. 从一道简单题说起 hdu 2089 "不要62" 一个数字,如果包含'4'或者'62', ...

  3. 洛谷P4317 花(fa)神的数论题(数位dp解法)

    日常废话: 完了高一开学第二天作业就写不完了药丸(其实第一天就写不完了) 传传传传传送 显然爆搜肯定过不了这道题但是有60分 我们注意到在[1,n]中,有着相同的1的个数的数有很多.若有x个数有i个1 ...

  4. 数位DP复习笔记

    前言 复习笔记第五篇.(由于某些原因(见下),放到了第六篇后面更新)CSP-S RP++. luogu 的难度评级完全不对,所以换了顺序,换了别的题目.有点乱,见谅.要骂就骂洛谷吧,原因在T2处 由于 ...

  5. 【算法】数位 dp

    时隔多日,我终于再次开始写博客了!! 上午听了数位 dp,感觉没听懂,于是在网上进行一番愉 ♂ 快 ♀ 的学习后,写篇博来加深一下印象~~ 前置的没用的知识 数位 不同计数单位,按照一定顺序排列,它们 ...

  6. 【BZOJ1662】[Usaco2006 Nov]Round Numbers 圆环数 数位DP

    [BZOJ1662][Usaco2006 Nov]Round Numbers 圆环数 Description 正如你所知,奶牛们没有手指以至于不能玩"石头剪刀布"来任意地决定例如谁 ...

  7. bzoj1026数位dp

    基础的数位dp 但是ce了一发,(abs难道不是cmath里的吗?改成bits/stdc++.h就过了) #include <bits/stdc++.h> using namespace ...

  8. HDU2089 不要62[数位DP]

    不要62 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  9. 数位DP GYM 100827 E Hill Number

    题目链接 题意:判断小于n的数字中,数位从高到低成上升再下降的趋势的数字的个数 分析:简单的数位DP,保存前一位的数字,注意临界点的处理,都是套路. #include <bits/stdc++. ...

随机推荐

  1. windows10下成功安装docker splash及遇到问题的解决方案

    转载出处:http://www.cnblogs.com/321lxl/p/9536616.html

  2. Vue-项目重要配置

    Vue配置axios ''' 1)安装插件(一定要在项目目录下): >: cnpm install axios 2)在main.js中配置: import axios from 'axios' ...

  3. spark-聚合算子aggregatebykey

    spark-聚合算子aggregatebykey Aggregate the values of each key, using given combine functions and a neutr ...

  4. WebUI自动化之Java语言提高

    单独写一个函数和把函数写在类中的区别: 单独写一个函数,函数只能完成一个功能,团队开发.让第三方使用时比较麻烦: 项目管理和构建自动化工具Maven:

  5. Luogu P1092 虫食算 爆搜

    心路历程:震惊,我竟然得了$90$分!!...康康数据...奥..(忽然有了邪恶的卡数据的想法) 于是把$for(int \space i=0;i<n;++i)$改成了$for(int \spa ...

  6. 《30天自制操作系统》学习笔记--番外篇之Mac环境下的工具介绍

    这几天又有点不务正业了,书也没看,一直在搞这个破环境,尝试各种做法,网上各种垃圾信息,浪费了很多时间,说的基本都是废话,不过还是找到了一些,赶紧写下来,不然这个过几天又忘了 首先是环境,我用的是Max ...

  7. Java 面试题 三 <JavaWeb应用调优线程池 JVM原理及调优>

    1.Java Web应用调优线程池 不论你是否关注,Java Web应用都或多或少的使用了线程池来处理请求.线程池的实现细节可能会被忽视,但是有关于线程池的使用和调优迟早是需要了解的.本文由浅入深,介 ...

  8. linux上安装nginx详细步骤

    一.安装依赖包 yum install gcc gcc-c++ pcre-devel patch libffi-devel python-devel zlib-devel bzip2-devel op ...

  9. pycharm同一目录下无法import其他文件

    如图:会出现带有红色波浪线,但是确实有random_walk文件 解决方法: 在当前文件下,右键找到mark  Directory as 然后选择source root,完工ok 再如图: 版权声明: ...

  10. 关于kafka定期清理日志后再消费报错kafka.common.OffsetOutOfRangeException的解决

    环境: kafka  0.10 spark  2.1.0 zookeeper  3.4.5-cdh5.14.0 公司阿里云测试机,十月一放假前,没有在继续消费,假期过后回来再使用spark strea ...