数位dp初等专题小总结
2016-08-01 12:27
323 查看
问题来源于kuangbin数位dp专题。。
数位dp , 记忆化搜索;
所以 他的时间复杂度要平均化
个人喜欢dfs版本的,因为感觉简单
然而专题中的题目已经过期了。。
hdu 2089 不要62
最简单的一种。。
hdu 3652 B-number
hdu 3555 bomb
codeforces 55D 这真是极好的题啊。。 对于离散化的只是又熟悉了一遍。。2520是123456789的最小公倍数,然而0-2520中许多数字是不需要的。。
poj 3252 Round Numbers
前导零这种东西用zero标记一下就结束了。。 讲道理pre1的效果中已经有了。只要有规则,任何进制都是用数位dp极好的。。
hdu 3709 Balanced Number
hdu 4734 F(x)
spoj Balanced Numbers
一开始智障一样的想用数组维护每个数字的奇偶性,发现要20*3的10次方,要爆炸。。然后就想到3进制了。。愉快的第一发T了。。原来是I64d。。欸。。好气啊。。
HDU 4507 吉哥系列故事——恨7不成妻 这是一道很好的题目,知道做到这道题才算是理解了数位dp的原理。数位dp每一位保存的代表的是具有相同特征的东西。而且和其他位置保存的值是互斥的。所以直接返回平方的做法是错误的
hdu 4352 XHXJ’s LIS
最后感觉就像做模拟。。。
数位dp , 记忆化搜索;
所以 他的时间复杂度要平均化
个人喜欢dfs版本的,因为感觉简单
然而专题中的题目已经过期了。。
hdu 2089 不要62
最简单的一种。。
#include <cstdio> #include <iostream> #include <cstring> using namespace std; int dig[8]; int dp[8][10]; int dfs(int len , int pre , int flag){//长度,前面传递的信息,是否受到目标的限制 if(len < 0){ return 1; } if(!flag && dp[len][pre] != - 1){//记忆话搜索 return dp[len][pre]; } int end = flag ? dig[len] : 9;//判断是否受到限制 int ans = 0; for(int i = 0 ; i <= end ; ++ i){//在遇到非法情况的时候直接斩掉。。当然也可以保存所有的非法情况再用合法的减去。因为这题数据范围小,随意 if(pre == 6 && i == 2){ continue; } if(i == 4){ continue; } ans += dfs(len - 1 , i , flag && i == end);//结尾的标记 } if(!flag){ dp[len][pre] = ans; } return ans; } int solve(int x){ int cnt = 0; while(x){ dig[cnt ++] = x % 10; x = x / 10; } return dfs(cnt - 1 , 0 , 1); } int main(){ int l , r; memset(dp , -1 , sizeof(dp)); while(~scanf("%d%d" , &l , &r)){ if(l == 0 && r == 0){ break; } printf("%d\n" , solve(r) - solve(l - 1)); } return 0; }
hdu 3652 B-number
#include <cstdio> #include <cstring> #include <iostream> using namespace std; int dig[10]; int dp[10][13][3];//位数,总和,是否出现13 int dfs(int len , int pre1 , int pre2 , int flag){ if(len < 0){ return pre1 == 0 && pre2 == 2; } if(!flag && dp[len][pre1][pre2] != -1){ return dp[len][pre1][pre2]; } int end = flag ? dig[len] : 9; int ans = 0; for(int i = 0 ; i <= end ; ++ i){ if(pre2 == 1 && i == 3){ ans += dfs(len - 1 , (pre1 * 10 + i) % 13 , 2 , flag && i == end); } else if(pre2 == 2){ ans += dfs(len - 1 , (pre1 * 10 + i) % 13 , 2 , flag && i == end); } else if(i == 1){ ans += dfs(len - 1 , (pre1 * 10 + i) % 13 , 1 , flag && i == end); } else{ ans += dfs(len - 1 , (pre1 * 10 + i) % 13 , 0 , flag && i == end); } } if(!flag){ dp[len][pre1][pre2] = ans; } return ans; } int solve(int x){ int cnt = 0; while(x){ dig[cnt ++] = x % 10; x = x / 10; } return dfs(cnt - 1 , 0 , 0 , 1); } int main(){ int n; memset(dp , -1 , sizeof(dp)); while(~scanf("%d" , &n)){ printf("%d\n" , solve(n)); } return 0; }
hdu 3555 bomb
#include <cstdio> #include <cstring> #include <iostream> using namespace std; #define ULL unsigned long long int dig[64]; ULL dp[64][3]; ULL dfs(int len , int pre , int flag){ if(len < 0){ return pre == 2; } if(!flag && dp[len][pre] != -1){ return dp[len][pre]; } int end = flag ? dig[len] : 9; ULL ans = 0; for(int i = 0 ; i <= end ; ++ i){ if(pre == 1 && i == 9){ ans += dfs(len - 1 , 2 , flag && i == end); } else if(pre == 2){ ans += dfs(len - 1 , 2 , flag && i == end); } else if(i == 4){ ans += dfs(len - 1 , 1 , flag && i == end); } else{ ans += dfs(len - 1 , 0 , flag && i == end); } } if(!flag){ dp[len][pre] = ans; } return ans; } ULL solve(ULL x){ int cnt = 0; while(x){ dig[cnt ++] = x % 10; x = x / 10; } return dfs(cnt - 1 , 0 , 1); } int main(){ int T; scanf("%d" , &T); memset(dp , -1 , sizeof(dp)); while(T --){ ULL n; scanf("%I64u" , &n); printf("%I64u\n" , solve(n)); } return 0; }
codeforces 55D 这真是极好的题啊。。 对于离散化的只是又熟悉了一遍。。2520是123456789的最小公倍数,然而0-2520中许多数字是不需要的。。
#include <cstdio> #include <iostream> #include <cstring> #include <algorithm> using namespace std; #define LL long long int dig[20]; LL dp[20][2520][50]; int all[2521];//离散化 int gcd(int x , int y){ if(x > y){ swap(x , y); } return x == 0 ? y : gcd(y % x , x); } void init(){ int tot = 0; for(int i = 1 ; i <= 2520 ; ++ i){ if(2520 % i == 0){ all[i] = tot ++; } } } LL dfs(int len , int pre1 , int pre2 , int flag){ if(len < 0){ return pre1 % pre2 == 0; } if(!flag && dp[len][pre1][all[pre2]] != -1){ return dp[len][pre1][all[pre2]]; } int end = flag ? dig[len] : 9; LL ans = 0; for(int i = 0 ; i <= end ; ++ i){ ans += dfs(len - 1 , (pre1 * 10 + i) % 2520 , i == 0 ? pre2 : pre2 * i / gcd(pre2 , i) , flag && i == end); } if(!flag){ dp[len][pre1][all[pre2]] = ans; } return ans; } LL solve(LL x){ int cnt = 0; while(x){ dig[cnt ++] = x % 10; x = x / 10; } return dfs(cnt - 1 , 0 , 1 , 1); } int main(){ int T; scanf("%d" , &T); init(); memset(dp , -1 , sizeof(dp)); while(T --){ LL l , r; scanf("%I64d%I64d" , &l , &r); printf("%I64d\n" , solve(r) - solve(l - 1)); } return 0; }
poj 3252 Round Numbers
前导零这种东西用zero标记一下就结束了。。 讲道理pre1的效果中已经有了。只要有规则,任何进制都是用数位dp极好的。。
#include <cstdio> #include <cstring> #include <iostream> using namespace std; int dig[35]; int dp[35][35][35]; int dfs(int len , int pre0 , int pre1 , int flag , int zero){ if(len < 0){ return pre1 <= pre0; } if(!flag && dp[len][pre1][pre0] != -1){ return dp[len][pre1][pre0]; } int end = flag ? dig[len] : 1; int ans = 0; for(int i = 0 ; i <= end ; ++ i){ if(i == 0) ans += dfs(len - 1 , zero ? 0 : pre0 + 1 , pre1 , flag && i == end , zero && !i); else ans += dfs(len - 1 , pre0 , pre1 + 1 , flag && i == end , 0); } if(!flag){ dp[len][pre1][pre0] = ans; } return ans; } int solve(int x){ int cnt = 0; while(x){ dig[cnt ++] = (x & 1); x >>= 1; } return dfs(cnt - 1 , 0 , 0 , 1 , 1); } int main(){ int l , r; memset(dp , -1 , sizeof(dp)); while(~scanf("%d%d" , &l , &r)){ printf("%d\n" , solve(r) - solve(l - 1)); } return 0; }
hdu 3709 Balanced Number
#include <cstdio> #include <algorithm> #include <cstring> #include <iostream> using namespace std; #define LL long long int dig[20]; LL dp[20][20][2000]; LL dfs(int len , int pre , int mid , int flag){ if(len < 0){ return pre == 0; } if(!flag && dp[len][mid][pre] != -1){ return dp[len][mid][pre]; } int end = flag ? dig[len] : 9; LL ans = 0; for(int i = 0 ; i <= end ; ++ i){ ans += dfs(len - 1 , (len - mid) * i + pre , mid , flag && i == end); } if(!flag){ dp[len][mid][pre] = ans; } return ans; } LL solve(LL x){ int cnt = 0; while(x){ dig[cnt ++] = x % 10; x = x / 10; } LL ans = 0; for(int i = 0 ; i < cnt ; ++ i){ ans += dfs(cnt - 1 , 0 , i , 1); } return ans - cnt + 1; } int main(){ int T; scanf("%d" , &T); memset(dp , -1 , sizeof(dp)); while(T --){ LL l , r; scanf("%I64d%I64d" , &l , &r); printf("%I64d\n" , solve(r) - solve(l - 1)); } return 0; }
hdu 4734 F(x)
#include <cstdio> #include <cstring> #include <iostream> using namespace std; #define LL long long int dig[15]; LL fa; LL dp[15][10240]; LL make(LL x){ LL now = 1; LL ans = 0; while(x){ ans += x % 10 * now; x = x / 10; now = (now << 1); } return ans; } LL dfs(int len , int pre , int flag){ if(len < 0){ return pre >= 0; } if(!flag && dp[len][pre] != -1){ return dp[len][pre]; } int end = flag ? dig[len] : 9; LL ans = 0; LL now = (1 << len); for(int i = 0 ; i <= end ; ++ i){ if(pre - i * now < 0){ break; } ans += dfs(len - 1 , pre - i * now , flag && i == end); } if(!flag){ dp[len][pre] = ans; } return ans; } LL solve(LL x){ int cnt = 0; while(x){ dig[cnt ++] = x % 10; x = x / 10; } return dfs(cnt - 1 , fa , 1); } int main(){ int T; int cas = 0; scanf("%d" , & T); memset(dp , -1 , sizeof(dp)); while(T --){ LL l , r; scanf("%I64d%I64d" , &l , &r); fa = make(l); printf("Case #%d: %I64d\n" , ++ cas , solve(r)); } return 0; }
spoj Balanced Numbers
一开始智障一样的想用数组维护每个数字的奇偶性,发现要20*3的10次方,要爆炸。。然后就想到3进制了。。愉快的第一发T了。。原来是I64d。。欸。。好气啊。。
#include <cstdio> #include <iostream> #include <cstring> #include <cmath> using namespace std; #define LL long long LL dp[20][81 * 81 * 9 + 10]; int dig[20]; LL Pow(int a , int b){ LL ans = 1; while(b){ if(b & 1){ ans *= a; } a = a * a; b >>= 1; } return ans; } int change(int pre , int i){ int test = pre; int cnt = 0; while(pre){ if(cnt == i){ break; } pre = pre / 3; cnt ++; } int fin = pre % 3; if(fin == 2){ return test - Pow(3 , i); } else{ return test + Pow(3 , i); } } int check(int x){ if(x == 0){ return 0; } int cnt = 0; while(x){ int test = x % 3; x = x / 3; if(test == 0){ cnt ++; continue ; } if(cnt & 1){ if(test & 1){ return 0; } } else{ if(!(test & 1)){ return 0; } } cnt ++; } return 1; } LL dfs(int len , int pre , int flag , int zero){ if(len < 0){ return check(pre); } if(!flag && dp[len][pre] != -1){ return dp[len][pre]; } int end = flag ? dig[len] : 9; LL ans = 0; for(int i = 0 ; i <= end ; ++ i){ if(zero && i == 0){ ans += dfs(len - 1 , 0 , flag && i == end , 1); } else{ ans += dfs(len - 1 , change(pre , i) , flag && i == end , 0); } } if(!flag){ dp[len][pre] = ans; } return ans; } LL solve(LL x){ int cnt = 0; while(x){ dig[cnt ++] = x % 10; x = x / 10; } return dfs(cnt - 1 , 0 , 1 , 1); } int main(){ int T; scanf("%d" , &T); memset(dp , -1 , sizeof(dp)); while(T --){ LL l , r; scanf("%lld%lld" , &l , &r); printf("%lld\n" , solve(r) - solve(l - 1)); } return 0; }
HDU 4507 吉哥系列故事——恨7不成妻 这是一道很好的题目,知道做到这道题才算是理解了数位dp的原理。数位dp每一位保存的代表的是具有相同特征的东西。而且和其他位置保存的值是互斥的。所以直接返回平方的做法是错误的
#include <cstdio> #include <cstring> #include <iostream> using namespace std; #define LL long long const LL mod = 1e9 + 7; struct all{ LL cnt; LL sum1; LL fin; all(){}; all(LL a , LL b , LL c){ cnt = a; sum1 = b; fin = c; } }dp[20][10][10]; int dig[20]; LL loc[20]; all dfs(int len , int pre1 , int pre2 , int flag){ if(len < 0){ all ans = all(0 , 0 , 0); ans.cnt = (pre1 != 0 && pre2 != 0);//?????? return ans; } if(!flag && dp[len][pre1][pre2].cnt != -1){ return dp[len][pre1][pre2]; } int end = flag ? dig[len] : 9; all ans = all(0 , 0 , 0); for(int i = 0 ; i <= end ; ++ i){ if(i == 7){ continue; } all tmp = dfs(len - 1 , (pre1 * 10 + i) % 7 , (pre2 + i) % 7 , flag && i == end);//???????? ans.cnt = (ans.cnt + tmp.cnt) % mod; ans.sum1 = (ans.sum1 + (tmp.sum1 + ((i * loc[len]) % mod * tmp.cnt) % mod) % mod) % mod; ans.fin = (ans.fin + (tmp.fin + (((i * loc[len]) % mod * (i * loc[len]) % mod) % mod * tmp.cnt % mod + (i * loc[len]) % mod * tmp.sum1 % mod * 2 % mod) % mod) % mod) % mod; } if(!flag){ dp[len][pre1][pre2] = ans; } return ans; } LL solve(LL x){ int cnt = 0; while(x){ dig[cnt ++] = x % 10; x = x / 10; } return dfs(cnt - 1 , 0 , 0 , 1).fin; } void init(){ loc[0] = 1; for(int i = 1 ; i <= 19 ; ++ i){ loc[i] = loc[i - 1] * 10 % mod; } } int main(){ int T; scanf("%d" , &T); memset(dp , -1 , sizeof(dp)); init(); while(T --){ LL l , r; scanf("%I64d%I64d" , &l , &r); //cout<<solve(9)<<endl; printf("%I64d\n" , (solve(r) - solve(l - 1) + mod) % mod); } return 0; }
hdu 4352 XHXJ’s LIS
最后感觉就像做模拟。。。
#include <cstdio> #include <iostream> #include <cstring> using namespace std; #define LL long long int k; int dig[64]; LL dp[64][1 << 10][11]; int change(int x , int i){ int t = x; for(int j = i ; j <= 10 ; ++ j){ if(t & (1 << j)){ t = (t ^ (1 << j)); break; } } return t | (1 << i); } int check(int x){ int cnt = 0; while(x){ if(x & 1){ ++ cnt; } x >>= 1; } if(cnt == k){ return 1; } else{ return 0; } } LL dfs(int len , int pre , int flag , int zero){ if(len < 0){ return check(pre); } if(!flag && dp[len][pre][k] != -1){ return dp[len][pre][k]; } int end = flag ? dig[len] : 9; LL ans = 0; for(int i = 0 ; i <= end ; ++ i){ if(zero && i == 0){ ans += dfs(len - 1 , 0 , flag && i == end , 1); } else{ ans += dfs(len - 1 , change(pre , i) , flag && i == end , 0); } } if(!flag){ dp[len][pre][k] = ans; } return ans; } LL solve(LL x){ int cnt = 0; while(x){ dig[cnt ++] = x % 10; x = x / 10; } return dfs(cnt - 1 , 0 , 1 , 1); } int main(){ int T; scanf("%d" , &T); int cas = 0; memset(dp , -1 , sizeof(dp)); while(T --){ LL l , r; scanf("%I64d%I64d%I64d" , &l , &r , &k); printf("Case #%d: %I64d\n" , ++ cas , solve(r) - solve(l - 1)); } return 0; }
相关文章推荐
- 逊哥dp专题 总结(普通dp,斜率优化dp,数位dp)
- 数位DP专题总结
- [专题总结]数位DP
- 专题(弱点)Dp训练总结【状压Dp*1+区间Dp*5+数位dp*3+树型Dp*2】【10/11】
- 数位DP专题小结--by sgx 数位DP专题小结--by sgx
- 数位dp专题
- 数位DP专题(开坑。
- 数位dp专题 (HDU 4352 3652 3709 4507 CodeForces 55D POJ 3252)
- 数位dp专题
- 数位DP专题
- 数位DP专题
- [kuangbin带你飞]专题十五 数位DP
- acdream 数树专题--完美数(数位dp)
- 【专题】数位DP(按位DP)
- 数位DP总结
- dp专题总结
- 数位dp总结
- 【专题】数位DP(按位DP)
- 数位统计/数位DP 专题
- 【专题总结】概率&期望DP