数位dp(洛谷 P4124 [CQOI2016]手机号码)
Posted Kalzn
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数位dp(洛谷 P4124 [CQOI2016]手机号码)相关的知识,希望对你有一定的参考价值。
题目链接
普通数位dp,不过有几点值得深思。
首先,我们每个手机号的位数固定为11位,即,不满足11位的并不是手机号不满足条件。而且如果像普通数位dp那样求该数到0之间的数量的话,还要考虑前导0的印象。(多开一维数组存前导0)不太值得。所以,我们直接求该数到1e10之间的数:
int mx = flag?su[cur]:9;
int bon = (cur==len-1)?1:0;
ll ans = 0;
for (int i = bon; i<=mx; i++)
//...........
此后我们设:
dp[i][p1][p2][sg1][sg2] 为考虑前i位,最后一个数字为p2,倒数第二个数字为p1,已有三个连续数相等(sg1==1)、没有三个连续数相等(sg1 ==0)的情况。其中8、4存在的情况由sg2决定:
sg2 == 0 无4、无8
sg2 == 1 有4、无8
sg2 == 2 无4、有8
sg2 == 3 有4、有8
然后就是标准数位dp过程
下面是ac代码:
压行版:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <algorithm>
#include <cstdlib>
#include <queue>
#include <map>
#include <vector>
#define ll long long
using namespace std;
int dp[20][11][11][5][5];
int su[20];
int len;
ll dfs(int cur, int pre1, int pre2, int sg1, int sg2, bool flag)
//cout << cur << " " << pre1 << " " << pre2 <<" " << sg1 << " " <<sg2 << endl;
if (cur < 0)
return sg1 && sg2 != 3;
if (!flag&&dp[cur][pre1][pre2][sg1][sg2] != -1) return dp[cur][pre1][pre2][sg1][sg2];
int mx = flag?su[cur]:9;
int bon = (cur==len-1)?1:0;
ll ans = 0;
// cout << bon <<" " << mx << endl;
for (int i = bon; i<=mx; i++)
ans += dfs(cur-1, pre2, i, sg1||(i == pre1 && i == pre2), sg2|(i==4||i==8?(1<<(i/4-1)):0), flag&&i==mx);//转移
if (!flag) dp[cur][pre1][pre2][sg1][sg2] = ans;
return ans;
ll so(ll n)
len = 0;
while(n)
su[len++] = n%10;
n /= 10;
if (len != 11) return 0;//注意特判,因为这里wa了一发,因为l-1可能是9999999999.
return dfs(len-1, 10, 10, 0, 0, 1);
int main()
ll r, l;
scanf("%lld%lld", &l, &r);
memset(dp, -1, sizeof(dp));
printf("%lld\\n", so(r) - so(l-1));
放飞自我版:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <algorithm>
#include <cstdlib>
#include <queue>
#include <map>
#include <vector>
#define ll long long
using namespace std;
int dp[20][11][11][5][5];
int su[20];
int len;
ll dfs(int cur, int pre1, int pre2, int sg1, int sg2, bool flag)
//cout << cur << " " << pre1 << " " << pre2 <<" " << sg1 << " " <<sg2 << endl;
if (cur < 0)
return sg1 && sg2 != 3;
if (!flag&&dp[cur][pre1][pre2][sg1][sg2] != -1) return dp[cur][pre1][pre2][sg1][sg2];
int mx = flag?su[cur]:9;
int bon = (cur==len-1)?1:0;
ll ans = 0;
// cout << bon <<" " << mx << endl;
for (int i = bon; i<=mx; i++)
int nowsg2;
if (i == 4)
if (sg2 == 0) nowsg2 = 1;
else if (sg2 == 1) nowsg2 = 1;
else if (sg2 == 2) nowsg2 = 3;
else nowsg2 = 3;
else if (i == 8)
if (sg2 == 0) nowsg2 = 2;
else if (sg2 == 1) nowsg2 = 3;
else if (sg2 == 2) nowsg2 = 2;
else nowsg2 = 3;
else nowsg2 = sg2;//找当前sg2
int nowsg1;
if (sg1 == 1) nowsg1 = 1;
else
if (cur == len-1 || cur == len-2) nowsg1 = 0;
else
if (i == pre1 && i == pre2) nowsg1 = 1;
else nowsg1 = 0;
//找当前sg1
ans += dfs(cur-1, pre2, i, nowsg1, nowsg2, flag&&i==mx);//转移
if (!flag) dp[cur][pre1][pre2][sg1][sg2] = ans;
return ans;
ll so(ll n)
len = 0;
while(n)
su[len++] = n%10;
n /= 10;
if (len != 11) return 0;//注意特判,因为这里wa了一发,因为l-1可能是9999999999.
return dfs(len-1, 0, 0, 0, 0, 1);
int main()
ll r, l;
scanf("%lld%lld", &l, &r);
memset(dp, -1, sizeof(dp));
ll ansr = so(r);
ll ansl = so(l-1);
printf("%lld\\n", ansr - ansl);
以上是关于数位dp(洛谷 P4124 [CQOI2016]手机号码)的主要内容,如果未能解决你的问题,请参考以下文章