数位dp
数位dp:f[i][j] 表示i位数满足j的情况下的个数,然后用记忆化搜索的写法来解决转移和统计的问题。
HDU3555 Bomb
题意:求1到n中含49的数的个数。
思路:·pos:当前位数。
·st:高位状态(st=0:前几位中没有49;st=1:前一位是4;st=2:前几位中有49)。
·flag:高位是否是原数。
#include<bits/stdc++.h> using namespace std; typedef long long ll; int bit[50]; ll f[50][5]; ll dfs(int pos,int st,bool flag) { //pos:当前位数 //st:高位状态 st=0:没有49;st=1:前一位是4;st=2:前几位中有49 //flag:高位是否是原数 if(pos == 0) return st == 2; if(flag && f[pos][st] != -1) return f[pos][st]; ll ans = 0; int up = flag ? 9 : bit[pos]; for(int i = 0; i <= up; i++) { if(st == 2 || st == 1 && i == 9){ ans += dfs(pos-1,2,flag || i < up); } else if(i == 4){ ans += dfs(pos-1,1,flag || i < up); } else{ ans += dfs(pos-1,0,flag || i < up); } } if(flag) f[pos][st] = ans; return ans; } ll solve(ll x) { int len = 0; while(x){ bit[++len] = x % 10; x /= 10; } return dfs(len,0,false); } int main() { int t; memset(f,-1,sizeof(f)); scanf("%d",&t); while(t--) { ll num;scanf("%lld",&num); printf("%lld\n",solve(num)); } return 0; }
HDU3652 B-number
题意:求1到n中含有13且能被13整除的数有多少个。
思路:含有13的条件与上一题类似。对于整除13,利用同余把除以13的余数也计入状态,即多出一个mod的状态,dfs为int dfs(int pos,int mod,int st,int flag)。
#include<bits/stdc++.h> using namespace std; typedef long long ll; int bit[20]; int f[20][5][15]; int dfs(int pos,int mod,int st,bool flag) { if(pos == 0 ) return st == 2 && mod == 0; if(flag && f[pos][st][mod] != -1) return f[pos][st][mod]; int ans = 0; int up = flag ? 9 : bit[pos]; for(int i = 0; i <= up; i++) { int _mod = mod * 10 + i; _mod %= 13; if(st == 2 || st == 1 && i == 3){ ans += dfs(pos-1,_mod,2,flag || i < up); } else if(i == 1){ ans += dfs(pos-1,_mod,1,flag || i < up); } else{ ans += dfs(pos-1,_mod,0,flag || i < up); } } if(flag) f[pos][st][mod] = ans; return ans; } int solve(int x) { int len = 0; while(x){ bit[++len] = x % 10; x /= 10; } return dfs(len,0,0,false); } int main() { memset(f,-1,sizeof(f)); int num; while(~scanf("%d",&num)) { printf("%d\n",solve(num)); } return 0; }
HDU4389 X mod f(x)
题意:计算区间内满足数字各位之和能整除该数字的整数个数。
思路:又和上一题有一点点像😅。由于各数位之和为1到81,所以可以对每一个数都进行一次操作就可以了。f[pos][mod][num][sum]表示前pos位数,除以num的余数为mod,各数位之和为sum的满足条件的数的个数。
#include<bits/stdc++.h> using namespace std; typedef long long ll; int bit[20]; int f[10][85][85][85]; int dfs(int pos,int mod,int num,int sum,bool flag) { if(pos == 0 ) return sum == num && mod % sum == 0; if(flag && f[pos][mod][num][sum] != -1) return f[pos][mod][num][sum]; int ans = 0; int up = flag ? 9 : bit[pos]; for(int i = 0; i <= up; i++) { int _mod = mod * 10 + i; _mod %= num; ans += dfs(pos-1,_mod,num,sum+i,flag || i <up); } if(flag) f[pos][mod][num][sum] = ans; return ans; } int solve(int x) { int len = 0; while(x){ bit[++len] = x % 10; x /= 10; } int ret = 0; for(int i=1;i<=81;i++) { ret += dfs(len,0,i,0,false); } return ret; } int main() { memset(f,-1,sizeof(f)); int t;scanf("%d",&t); for(int cas = 1; cas <= t; cas++) { int l,r; scanf("%d%d",&l,&r); int ans = solve(r) - solve(l-1); printf("Case %d: %d\n",cas,ans); } return 0; }
HDU2089 不要62
一道简单的数位dp。
#include<bits/stdc++.h> using namespace std; typedef long long ll; int a[20]; int dp[20][2]; int dfs(int pos,int pre,int sta,bool limit) { if(pos==-1) return 1; if(!limit && dp[pos][sta]!=-1) return dp[pos][sta]; int up=limit ? a[pos] : 9; int tmp=0; for(int i=0;i<=up;i++){ if(pre==6 && i==2)continue; if(i==4) continue; tmp+=dfs(pos-1,i,i==6,limit && i==a[pos]); } if(!limit) dp[pos][sta]=tmp; return tmp; } int solve(int x) { int pos=0; while(x){ a[pos++]=x%10; x/=10; } return dfs(pos-1,-1,0,true); } int main() { int le,ri; while(~scanf("%d%d",&le,&ri) && le+ri){ memset(dp,-1,sizeof dp); printf("%d\n",solve(ri)-solve(le-1)); } return 0; }
HDU3709 Balanced Number
题意:求区间内平衡数的个数。平衡数的定义为:这个数字的某一位为支点,另外两边的数字大小乘以力矩之和相等(对于每一个数位)。
思路:·枚举支点,计算力矩,f[pos][x][st]表示pos位的数,支点是x,目前的力矩为st的数的个数。
·转移:f[pos][x][st] = Σf[pos-1][x][st + i * (pos - x)]。
#include<bits/stdc++.h> using namespace std; typedef long long ll; int bit[25]; ll f[25][25][2005]; ll dfs(int pos,int x,int st,bool flag) { if(pos == 0 ) return st == 0; if(flag && f[pos][x][st] != -1) return f[pos][x][st]; ll ans = 0; int up = flag ? 9 : bit[pos]; for(int i = 0; i <= up; i++) { ans += dfs(pos-1,x,st + i * (pos - x),flag || i < up); } if(flag) f[pos][x][st] = ans; return ans; } ll solve(ll x) { int len = 0; while(x){ bit[++len] = x % 10; x /= 10; } ll ret = 0; for(int i=1;i<=len;i++){ ret += dfs(len,i,0,false); } return ret - len + 1; } int main() { memset(f,-1,sizeof(f)); int t;scanf("%d",&t); for(int cas = 1; cas <= t; cas++) { ll l,r; scanf("%lld%lld",&l,&r); ll ans = solve(r) - solve(l-1); printf("%lld\n",ans); } return 0; }
浙公网安备 33010602011771号