数位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\)一般 ...
随机推荐
- linux kernel启动流程
linux kernel启动是从./init/main.c中开始的,其大概流程是: 1. 调用start_kernel()函数: 2. start_kernel()调用rest_init()函数: 3 ...
- Linux 线程属性函数总结
1.初始化一个线程对象的属性 int pthread_attr_init(pthread_attr_t *attr); 返回值:若是成功返回0,否则返回错误的编号 形 参: attr 指向一个线程属性 ...
- poj 1845 Sumdiv (数论)
题目链接 题意:求 A^B的所有约数之和对9901取模后的结果. 分析: 看了小优的博客写的. 分析来自 http://blog.csdn.net/lyy289065406/article/detai ...
- bzoj3244
很不幸,这题我又被虐了,给个链接http://www.cnblogs.com/g-word/p/3288675.html ..] of longint; f:..,..] of longint; mx ...
- eclipse无法与手机连上的解决方案
在eclipse上开发android应用,有时候会遇到eclipse无法识别手机的问题,就算把数据线拔了又插,插了又拔,哪怕是重启eclipse 甚至是重启电脑,这个问题也依然是解决不了.这时候就非常 ...
- Android实现全屏显示的方法
一种是在xml文件中设置相应属性,另一种是用代码实现. 1.在AndroidManifest.xml的配置文件里面的<activity>标签添加属性: android:theme=&quo ...
- UVA 11396 Claw Decomposition(二分图)
以“爪”形为单元,问所给出的无向图中能否被完全分割成一个个单元. 分析图的性质,由于已知每个点的度是3,所以“爪”之间是相互交错的,即把一个“爪”分为中心点和边缘点,中心点被完全占据,而边缘点被三个“ ...
- 解决魅族USB调试无法被电脑识别的问题(含Mac OS X、Win7)
每次打开豌豆荚或者360手机助手之类手机助手后Eclipse才会检测到mx4(实际上是豌豆荚关闭eclipse的adb使用自己的驱动连接的).解决方法就是在"adb_usb.ini&qu ...
- Android:真机调试,不显示logcat的解决方案
有时做开发的时候,用真机测试,总是看不到logcat信息 .原因是系统默认关闭了log,需要将其打开. 解决办法如下: 在拨号界面输入*#*#2846579#*#* ,然后系统会自动弹出一个菜单, ...
- 【DFS】NYOJ-325-zb的生日
[题目链接:NYOJ-325] 一道以我名字命名的题目,难道要我生日的时候再A? 思路:依旧深搜,但这个问题应该有一个专有名词吧,看别的博客说是 “容量为 sum/2 的背包问题”,不懂... // ...