这周开始刷数位DP,在网上找到一份神级数位DP模板,做起题目来爽歪歪。

http://www.cnblogs.com/jffifa/archive/2012/08/17/2644847.html

 int dfs(int i, int s, bool e) {
if (i==-) return s==target_s;
if (!e && ~f[i][s]) return f[i][s];
int res = ;
int u = e?num[i]:;
for (int d = first?:; d <= u; ++d)
res += dfs(i-, new_s(s, d), e&&d==u);
return e?res:f[i][s]=res;
}

f为记忆化数组;

i为当前处理串的第i位(权重表示法,也即后面剩下i+1位待填数);

s为之前数字的状态(如果要求后面的数满足什么状态,也可以再记一个目标状态t之类,for的时候枚举下t);

e表示之前的数是否是上界的前缀(即后面的数能否任意填)。

for循环枚举数字时,要注意是否能枚举0,以及0对于状态的影响,有的题目前导0和中间的0是等价的,但有的不是,对于后者可以在dfs时再加一个状态变量z,表示前面是否全部是前导0,也可以看是否是首位,然后外面统计时候枚举一下位数。It depends.

于是关键就在怎么设计状态。当然做多了之后状态一眼就可以瞄出来。

HDU2098 不要62

 #include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <utility>
#include <vector>
#include <queue>
using namespace std;
#define INF 0x3f3f3f3f
#define maxn 15
int dp[maxn][],num[maxn];
int new_s(int s, int d)
{
if (d == ) return ;
else
{
return ;
}
}
int dfs(int i, int s, bool e) {
if (i == -) return ;
if (!e && ~dp[i][s]) return dp[i][s];
int res = ;
int u = e ? num[i] : ;
for (int d = ; d <= u; ++d)
{
if (d==) continue;
if (s&&d==) continue;
res += dfs(i - , new_s(s, d), e && d == u);
}
return e ? res : dp[i][s] = res;
}
int cal(int n)
{
int cnt = ;
while (n)
{
num[cnt++] = n % ;
n /= ;
}
return dfs(cnt - , ,);
}
int main()
{
int l, r;
memset(dp, -, sizeof(dp));
while (scanf("%d%d", &l, &r) != EOF&&l+r)
{
printf("%d\n", cal(r) - cal(l -));
}
return ;
}

HDU3555 Bomb

和上题差不多

 #include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <utility>
#include <vector>
#include <queue>
using namespace std;
#define INF 0x3f3f3f3f
#define maxn 30
typedef long long LL;
LL dp[maxn][];
int num[maxn];
int new_s(int s, int d)
{
if (s == ) return ;
if (s == && d == ) return ;
if (d == ) return ;
return ; }
LL dfs(int i, int s, bool e)
{
if (i == -) return s == ;
if (!e&&~dp[i][s]) return dp[i][s];
LL ret = ;
int u = e ? num[i] : ;
for (int d = ; d <= u; d++)
ret += dfs(i - , new_s(s, d), e&&d == u);
return e ? ret : dp[i][s] = ret; }
LL cal(LL n)
{
int cnt = ;
while (n)
{
num[cnt++] = n % ;
n /= ;
}
return dfs(cnt - , , );
}
int main()
{
int T;
scanf("%d", &T);
memset(dp, -, sizeof(dp));
while (T--)
{
LL n;
scanf("%I64d", &n);
printf("%I64d\n", cal(n));
}
return ;
}

BZOJ1026 windy数

注意前导0

 #include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <utility>
#include <vector>
#include <queue>
using namespace std;
#define INF 0x3f3f3f3f
#define maxn 30
typedef long long LL;
LL dp[maxn][];
int num[maxn];
LL dfs(int i, int s, bool e,int pre)
{
if (i == -) return s == ;
if (!e&&~dp[i][pre]&&s) return dp[i][pre];
LL ret = ;
int u = e ? num[i] : ;
for (int d = ; d <= u; d++)
if(!s||abs(pre-d)>=)
{
ret += dfs(i - , s||d>, e&&d == u, d);
}
if (!e&&s)dp[i][pre] = ret;
return ret;
} LL cal(LL n)
{
int cnt = ;
while (n)
{
num[cnt++] = n % ;
n /= ;
}
return dfs(cnt - , , ,);
}
int main()
{
LL x,y;
memset(dp, -, sizeof(dp));
while (scanf("%lld%lld", &x,&y)!=EOF)
{
printf("%lld\n", cal(y)-cal(x-));
}
return ;
}

HDU3652 B-number

加一维,记录余数

 #include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <utility>
#include <vector>
#include <queue>
using namespace std;
#define INF 0x3f3f3f3f
#define maxn 30
typedef long long LL;
LL dp[maxn][][];
int num[maxn];
int new_s(int s, int d)
{
if (s == ) return ;
if (s == && d == ) return ;
if (d == ) return ;
return ;
}
LL dfs(int i, int s, bool e,int r)
{
if (i == -)
{
if ((s== ) && (r== )) return ;
else return ;
}
if (!e&&~dp[i][r][s]) return dp[i][r][s];
LL ret = ;
int u = e ? num[i] : ;
for (int d = ; d <= u; d++)
{
ret += dfs(i - ,new_s(s,d) , e&&d == u, (r*+d)%);
}
return e ? ret : dp[i][r][s] = ret;
} LL cal(LL n)
{
int cnt = ;
while (n)
{
num[cnt++] = n % ;
n /= ;
}
return dfs(cnt - , , ,);
}
int main()
{
LL n;
memset(dp, -, sizeof(dp));
while (scanf("%I64d",&n)!=EOF)
{
printf("%I64d\n", cal(n));
}
return ;
}

HDU3943 K-th Nya Number

需要二分答案,还有就是注意区间范围是[P+1,Q],被坑了好多发,还有我很不解的就是,memset放在最外面就会WA,每输入一组案例清空一次就AC了。。坑爹。。

 #include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <utility>
#include <vector>
#include <queue>
using namespace std;
#define INF 0x3f3f3f3f
#define maxn 25
typedef long long LL;
LL dp[maxn][maxn][maxn];
LL P, Q, x, y;
int num[maxn];
LL dfs(int i, int sx, int sy, bool e)
{
if (i == -)
{
if (sx == x&&sy == y) return ;
else return ;
}
if (!e&&~dp[i][sx][sy]) return dp[i][sx][sy];
LL ret = ;
int u = e ? num[i] : ;
for (int d = ; d <= u; d++)
{
if (sx == x&&d == ) continue;
if (sy == y&&d == ) continue;
int a, b;
a = sx; b = sy;
if (d == ) a++;
if (d == ) b++;
ret += dfs(i - , a, b, e&&d == u);
}
return e ? ret : dp[i][sx][sy] = ret;
}
LL cal(LL n)
{
int cnt = ;
while (n)
{
num[cnt++] = n % ;
n /= ;
}
return dfs(cnt - , , , );
}
LL Bin(LL k)
{
LL l, r, mid, ans,ret;
ans = ;
ret = cal(P);
l = P+; r = Q;
while (l <= r)
{
mid = (l + r) >> ;
if (cal(mid) - ret>= k)
{
ans = mid;
r = mid - ;
}
else l = mid + ;
}
return ans;
}
int main()
{
int T,kase=;
scanf("%d", &T); while (T--)
{
int n;
memset(dp, -, sizeof(dp));
scanf("%I64d%I64d%I64d%I64d", &P, &Q, &x, &y);
scanf("%d", &n);
printf("Case #%d:\n", ++kase);
while (n--)
{
LL k;
scanf("%I64d",&k);
LL ans = Bin(k);
if (ans)
printf("%I64d\n", ans);
else printf("Nya!\n");
}
}
return ;
}

POJ3208 Apocalypse Someday

做法和上题一样,需要注意的是它是必须有连续的三个6,还有就是二分的上界尽量大。。

 #include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <utility>
#include <vector>
#include <queue>
using namespace std;
#define INF 0x3f3f3f3f
#define maxn 30
typedef long long LL;
LL dp[maxn][];
int num[maxn];
int new_d(int s, int d)
{
if (s == ) return ;
int st = s;
if (d == ) s++;
return st==s?:s;
}
LL dfs(int i, int s,bool e)
{
if (i == -) return s == ;
if (!e&&~dp[i][s]) return dp[i][s];
LL ret = ;
int u = e ? num[i] : ;
for (int d = ; d <= u; d++)
{
ret += dfs(i - , new_d(s,d),e&&d == u);
}
return e ? ret : dp[i][s] = ret;
}
LL cal(LL n)
{
int cnt = ;
while (n)
{
num[cnt++] = n % ;
n /= ;
}
return dfs(cnt - , , );
}
LL Bin(LL k)
{
LL l, r, mid, ans,ret;
ans = ;
l = , r = 100000000000LL;
while (l <= r)
{
mid = (l + r) >> ;
if (cal(mid)>= k)
{
ans = mid;
r = mid - ;
}
else l = mid + ;
}
return ans;
}
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
LL k;
memset(dp, -, sizeof(dp));
scanf("%I64d", &k);
printf("%I64d\n", Bin(k));
}
return ;
}

SPOJ BALNUM Balanced Numbers

刚开始一直不知道该怎么记录前面的状态,搜了下解题报告,用的是三进制来表示前面的状态(此题的精华就是这里吧。。),因为状态总数为3^10,因此也不大。

 #include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <utility>
#include <vector>
#include <queue>
using namespace std;
#define INF 0x3f3f3f3f
#define maxn 66666
typedef long long LL;
LL dp[][maxn];
int num[];
int cnt[];
void go(int s)
{
for (int i = ; i < ; i++)
{
cnt[i] = s % ;
s /= ;
}
}
int new_s(int s, int d)
{
go(s);
if (cnt[d] == ) cnt[d] = ;
else
cnt[d] = - cnt[d];
int base = ;
s = ;
for (int i = ; i < ; i++)
{
s += base * cnt[i];
base *= ;
}
return s;
}
int check(int s)
{
go(s);
for (int i = ; i < ; i++)
{
if ((i & ) && (cnt[i] == )) return ;
if (!(i & ) && (cnt[i] == ))return ;
}
return ;
}
LL dfs(int i, int s, bool e,int zero)
{
if (i == -) return check(s);
if (!e&&~dp[i][s]) return dp[i][s];
LL ret = ;
int u = e ? num[i] : ;
for (int d = ; d <= u; d++)
{
ret += dfs(i - , zero&&d==?:new_s(s, d), e&&d == u,zero&&d==);
}
return e ? ret : dp[i][s] = ret;
}
LL cal(LL n)
{
int cnt = ;
while (n)
{
num[cnt++] = n % ;
n /= ;
}
return dfs(cnt - , , ,);
}
int main()
{
int T;
scanf("%d", &T);
memset(dp, -, sizeof(dp));
while (T--)
{
LL x, y;
scanf("%lld%lld", &x, &y);
printf("%lld\n", cal(y) - cal(x - ));
}
return ;
}

SPOJ MYQ10 Mirror Number

每个数字只可能是0,1,8,区间比较大0 <= a<=b <= 10^44,所以输入要用字符串,一般我们求答案都是:cal(b)-cal(a-1),但此题是字符串,因此需要特殊下a是不是Mirror Number,还被坑了好久的就是,由于是字符串输入,最高位的下标是0。。然后我没有反转过来。。找了好久才发现。。用一个数组记录前面选的数(之前的状态),只要第一个非0位确定就可以知道回文串的长度,也就知道回文串中心的位置,然后从中心更低的位置开始判断是不是回文。前导0也需要注意

 #include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <utility>
#include <vector>
#include <queue>
using namespace std;
#define INF 0x3f3f3f3f
#define maxn 50
typedef long long LL;
LL dp[maxn][maxn];
int num[maxn],tmp[maxn];
LL dfs(int i, int len, bool e,int zero)
{
if (i == -) return ;
if (!e&&~dp[i][len]) return dp[i][len];
LL ret = ;
int u = e ? num[i] : ;
for (int d = ; d <= u; d++)
{
if (d!=&&d!=&&d!=) continue;
if (zero)
{
tmp[i] = d;
ret += dfs(i - , len - !d, e&&d == u, zero&& d == );
}
else
{
int mid = len / ;
int fg = i < mid ? : ;
if (fg)
{
if (tmp[len - i - ] == d)
ret += dfs(i - , len, e&&d == u, zero);
}
else
{
tmp[i] = d;
ret += dfs(i - , len, e&&d == u, zero);
}
}
}
return e ? ret : dp[i][len] = ret;
}
LL cal(char *s)
{
int cnt = strlen(s);
for (int i = ; i < cnt; i++) num[cnt-i-] = s[i] - '';
return dfs(cnt - , cnt, ,);
}
int ok(char *s)
{
int len = strlen(s);
for (int i = ; i < len; i++)
if (s[i] != ''&& s[i] != ''&&s[i] != '') return ;
for (int i = ; i < len / ; i++)
if (s[i] != s[len - i - ]) return ;
return ;
}
int main()
{
int T;
scanf("%d", &T);
memset(dp, -, sizeof(dp));
while (T--)
{
char x[maxn], y[maxn];
scanf("%s%s", x, y);
printf("%lld\n", cal(y) - cal(x)+ok(x));
} return ;
}

数位DP专题的更多相关文章

  1. 蒟蒻的数位DP专题总结

    BZOJ  1026: [SCOI2009]windy数: 题目链接: http://www.lydsy.com/JudgeOnline/problem.php?id=1026           d ...

  2. 「动态规划」-数位dp专题

    数位dp,今天学长讲的稍玄学,课下花了一会时间仔细看了一下,发现板子是挺好理解的,就在这里写一些: 数位dp主要就是搞一些在区间中,区间内的数满足题目中的条件的数的个数的一类题,题目一般都好理解,这时 ...

  3. hdoj2089(入门数位dp)

    题目链接:https://vjudge.net/problem/HDU-2089 题意:给定一段区间求出该区间中不含4且不含连续的62的数的个数. 思路:这周开始做数位dp专题,给自己加油^_^,一直 ...

  4. [kuangbin带你飞]专题十五 数位DP

            ID Origin Title   62 / 175 Problem A CodeForces 55D Beautiful numbers   30 / 84 Problem B HD ...

  5. 专题训练之数位DP

    推荐以下一篇博客:https://blog.csdn.net/wust_zzwh/article/details/52100392 1.(HDOJ2089)http://acm.hdu.edu.cn/ ...

  6. 【专题】数位DP

    [资料] ★记忆化搜索:数位dp总结 之 从入门到模板 by wust_wenhao 论文:浅谈数位类统计问题 数位计数问题解法研究 [记忆化搜索] 数位:数字从低位到高位依次为0~len-1. 高位 ...

  7. UESTC 2015dp专题 H 邱老师选妹子 数位dp

    邱老师选妹子 Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://acm.uestc.edu.cn/#/contest/show/65 Descr ...

  8. 「kuangbin带你飞」专题十五 数位DP

    传送门 A.CodeForces - 55D Beautiful numbers 题意 一个正整数是 漂亮数 ,当且仅当它能够被自身的各非零数字整除.我们不必与之争辩,只需计算给定范围中有多少个漂亮数 ...

  9. 动态规划专题(三)——数位DP

    前言 数位\(DP\) 真的是最恶心的\(DP\). 简介 看到那种给你两个数,让你求这两个数之间符合条件的数的个数,且这两个数非常大,这样的题目一般就是 数位\(DP\) 题. 数位\(DP\)一般 ...

随机推荐

  1. Android Bundle的使用

    发送数据: Bundle bundle = new Bundle(); bundle.putString("sex" , "男人"); bundle.putDo ...

  2. Java API —— 递归

    1.方法定义中调用方法本身的现象 2.递归注意实现         1) 要有出口,否则就是死递归         2) 次数不能太多,否则就内存溢出         3) 构造方法不能递归使用 3. ...

  3. 关于java -version版本问题

    因为安装了Oracle,而Oracel会自带JDK,安装完成后,会自动把自己的JDK设置在最前面(path变量里). 这就是为什么结果与事实不相同的原因. 解决方法: 进入系统环境变量,找到path变 ...

  4. SimpleDateFormat日期格式化

    public class T { /** * @param args */ public static void main(String[] args) { // TODO Auto-generate ...

  5. 【HDOJ】1800 Flying to the Mars

    1. 题目描述挺简单的一道题,给定$n$个大整数,求出现最多的次数. 2. 基本思路这题是可以使用哈希做,ELFHash等哈希都可以过. 3. 代码 /* 1800 */ #include <i ...

  6. Android远程图片获取和本地缓存

    对于客户端——服务器端应用,从远程获取图片算是经常要用的一个功能,而图片资源往往会消耗比较大的流量,对 应用来说,如果处理不好这个问题,那会让用户很崩溃,不知不觉手机流量就用完了,等用户发现是你的应用 ...

  7. 解析UML9种图的作用

    本文和大家重点讨论一下UML9种图的概念,UML中有五类图,共有9种图形,每种图形都有各自的特点,下面就让我们一起来看一下这些图形特点的详细介绍吧. UML9种图简介 1.用例图 说明的是谁要使用系统 ...

  8. 将多个.a库合并为一个.a库的方法

    如果编译了多个架构的静态库,想将它们合并为一个静态库的时候,可以用如下方法合并: sudo lipo -create /libs/ffmpeg/2.6.3/arm64/lib/libavcodec.a ...

  9. c++实现输入法窗口自定义的代码

    #pragma once #include <Windows.h> #include <imm.h> #include <string> #pragma comme ...

  10. 用Rational Rose来建立数据库表

    这里以MS SQL Server2000中已有的一个Northwind库为例,我们命名新的数据库名为NorthwindRose:我们只挑其中的两个表Customers和Employees做示例,另外我 ...