数位DP专题
这周开始刷数位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专题的更多相关文章
- 蒟蒻的数位DP专题总结
BZOJ 1026: [SCOI2009]windy数: 题目链接: http://www.lydsy.com/JudgeOnline/problem.php?id=1026 d ...
- 「动态规划」-数位dp专题
数位dp,今天学长讲的稍玄学,课下花了一会时间仔细看了一下,发现板子是挺好理解的,就在这里写一些: 数位dp主要就是搞一些在区间中,区间内的数满足题目中的条件的数的个数的一类题,题目一般都好理解,这时 ...
- hdoj2089(入门数位dp)
题目链接:https://vjudge.net/problem/HDU-2089 题意:给定一段区间求出该区间中不含4且不含连续的62的数的个数. 思路:这周开始做数位dp专题,给自己加油^_^,一直 ...
- [kuangbin带你飞]专题十五 数位DP
ID Origin Title 62 / 175 Problem A CodeForces 55D Beautiful numbers 30 / 84 Problem B HD ...
- 专题训练之数位DP
推荐以下一篇博客:https://blog.csdn.net/wust_zzwh/article/details/52100392 1.(HDOJ2089)http://acm.hdu.edu.cn/ ...
- 【专题】数位DP
[资料] ★记忆化搜索:数位dp总结 之 从入门到模板 by wust_wenhao 论文:浅谈数位类统计问题 数位计数问题解法研究 [记忆化搜索] 数位:数字从低位到高位依次为0~len-1. 高位 ...
- UESTC 2015dp专题 H 邱老师选妹子 数位dp
邱老师选妹子 Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.uestc.edu.cn/#/contest/show/65 Descr ...
- 「kuangbin带你飞」专题十五 数位DP
传送门 A.CodeForces - 55D Beautiful numbers 题意 一个正整数是 漂亮数 ,当且仅当它能够被自身的各非零数字整除.我们不必与之争辩,只需计算给定范围中有多少个漂亮数 ...
- 动态规划专题(三)——数位DP
前言 数位\(DP\) 真的是最恶心的\(DP\). 简介 看到那种给你两个数,让你求这两个数之间符合条件的数的个数,且这两个数非常大,这样的题目一般就是 数位\(DP\) 题. 数位\(DP\)一般 ...
随机推荐
- 机器学习 —— 概率图模型(Homework: StructuredCPD)
Week2的作业主要是关于概率图模型的构造,主要任务可以分为两个部分:1.构造CPD;2.构造Graph.对于有向图而言,在获得单个节点的CPD之后就可依据图对Combine CPD进行构造.在获得C ...
- Linux使用者管理(1)---用户账号
linux很重要的应用就是作为服务器的操作系统.服务器的作用是给多用户提供各种“服务”(可能是读服务器上的文件,或者是利用服务器进行数值计算)那么如果多用户共同拥有一台服务器,就需要对服务器上的用户进 ...
- 如何构建你自己的Java库【翻译】
原文http://www.programcreek.com/2011/07/build-a-java-library-for-yourself/ 原文和翻译都只是参考,如有不对,欢迎指正. 代码复用是 ...
- 怎么在eclipse里调试WebDriver的源代码
当你看完WebDriver的工作原理这篇博客以后,是不是也跃跃欲试想印证文章里的理论是不是正确,想自己也看下webdriver的源代码,并且调试下,通过代码来更深入的了解WebDriver的工作原理. ...
- 详谈 Jquery Ajax 异步处理Json数据.
啥叫异步,啥叫Ajax.咱不谈啥XMLHTTPRequest.通俗讲异步就是前台页面javascript能调用后台方法.这样就达到了无刷新.所谓的Ajax.这里我们讲二种方法 方法一:(微软有自带Aj ...
- Android移动应用开发中常见的经验技巧总结
转:http://wwwdevstorecn/essay/essayInfo/6128.html 1. 对话保持的解决方案. 要求: 1.app中使用webview访问具体网站的内容,但是app与服务 ...
- UVa 1640 (计数) The Counting Problem
题意: 统计[a, b]或[b, a]中0~9这些数字各出现多少次. 分析: 这道题可以和UVa 11361比较来看. 同样是利用这样一个“模板”,进行区间的分块,加速运算. 因为这里没有前导0,所以 ...
- UVa 1103 (利用连通块来判断字符) Ancient Messages
本题就是灵活运用DFS来求连通块来求解的. 题意: 给出一幅黑白图像,每行相邻的四个点压缩成一个十六进制的字符.然后还有题中图示的6中古老的字符,按字母表顺序输出这些字符的标号. 分析: 首先图像是被 ...
- HDU 2087 (KMP不可重叠的匹配) 花布条
题意: 用两个字符串分别表示布条和图案,问能从该布条上剪出多少这样的图案. 分析: 毫无疑问这也是用KMP匹配,关键是一次匹配完成后,模式串应该向后滑动多少. 和上一题 HDU 1686 不同,两个图 ...
- [转] 搜索之双向BFS
转自:http://www.cppblog.com/Yuan/archive/2011/02/23/140553.aspx 如果目标也已知的话,用双向BFS能很大程度上提高速度. 单向时,是 b^le ...