数位DP小小结
FZOJ Problem 2113 Jason的特殊爱好
题意:x~y数字里面有多少个 1
思路:我们算法课实验题的简化版,当时我用了很麻烦的一个DP=_=
刚刚学到了很棒的姿势,记忆化DP!!
dfs(int pos ,bool end1) ;
end1==false 返回pos位后面(包含pos)任意组合有多少个 1 ;
end1==true 返回上一位是结尾,Pos以后的位受到限制组合有多少个 1 ;
大概是这样,如果数字是 4987 现在计算到 8 这个数字,end1==true,说明是49XX
XX的取值是0-87;
如果计算到当前位,如果是这位是1 ,如果没有限制,那么答案就是加上10^pos个
如果有限制,那么答案就是加上nn%(10^pos)+1 个,
如果不是结尾(也就是没有限制的),我们可以dp[pos]保存计算过的结果。
include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #include<vector> #include<set> #include<stack> #include<map> #include<ctime> #include<bitset> #define LL long long #define ll long long #define INF 0x3f3f3f3f #define maxn 50010 #define eps 1e-6 #define mod 1000000007 using namespace std; LL dp[30] ,nn ; int bit[30] ; LL get1(int pos) { LL tt=1; for(int i=0;i<pos;i++) tt *= 10; return nn%tt; } LL get2(int pos) { LL tt=1; for(int i=0;i<pos;i++) tt *= 10; return tt; } LL dfs(int pos ,bool end1) { if(pos==-1) { return 0; } if(!end1&&dp[pos] != -1) { return dp[pos]; } int e ,ee ; LL ans=0; if(end1) e = bit[pos] ; else e = 9 ; for(int i = 0 ; i <= e ;i++) { if(i==1){ if(i==e)ans += get1(pos)+1; else ans += get2(pos) ; } ans += dfs(pos-1,(end1&&i==e)) ; } if(!end1) { dp[pos] = ans; } return ans; } LL getans(LL n ) { nn=n; int len=0; if(n<=0) return 0 ; while(n) { bit[len++] = n%10 ; n /= 10 ; } LL ans=0; ans = dfs(len-1,1) ; return ans; } int main() { int i , j , m ,k ,T ; LL x,y ; memset(dp,-1,sizeof(dp)) ; // cout << getans(20)<<endl; while(cin >> x >> y) { y=getans(y) ; x=getans(x-1); cout << y-x<< endl; } return 0 ; }
hdu 3555 Bomb
题意:x~y数字里面有多少个49
思路:和上面做法差不多,只是多了一个状态
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #include<vector> #include<set> #include<stack> #include<map> #include<ctime> #include<bitset> #define LL long long #define ll long long #define INF 0x3f3f3f3f #define maxn 50010 #define eps 1e-6 #define mod 1000000007 using namespace std; LL dp[30][20] ,nn ; int bit[30] ; LL dfs(int pos ,int num ,bool end1) { if(pos==-1) { return num==2 ; } if(!end1&&dp[pos][num] != -1) { return dp[pos][num]; } int e,ee; LL ans=0; if(end1) e = bit[pos] ; else e = 9 ; for(int i = 0 ; i <= e ;i++) { if(num==1&&i==9) { ee=2; } else if(num==0&&i==4) { ee=1; } else if(num==1&&i != 4) { ee=0; } else ee=num ; ans += dfs(pos-1,ee,(end1&&i==e)) ; } if(!end1) { dp[pos][num] = ans; } return ans; } LL getans(LL n ) { nn=n; int len=0; if(n<=0) return 0 ; while(n) { bit[len++] = n%10 ; n /= 10 ; } LL ans=0; ans = dfs(len-1,0,1) ; return ans; } int main() { int i , j , m ,k ,T ; LL x,y ; memset(dp,-1,sizeof(dp)) ; // cout << getans(20)<<endl; cin >> T ; while(cin >> y) { y=getans(y) ; cout << y<< endl; } return 0 ; }
hdu 3709 Balanced Number
题意:x-y有多少个是平衡的,平衡的定义是,找一个支点,使得两边的重量一样
思路: 还是和上面的姿势一样,只是状态多了一些~~~
对于 dfs(int pos ,int v,int val,bool end1)
if(end1==false)表示当前计算到第pos位,选的支点为v,前面比后面的重val,后面pos位任意组合
返回的数字数目。
if(end1==true)也就是有限制了
include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #include<vector> #include<set> #include<stack> #include<map> #include<ctime> #include<bitset> #define LL long long #define ll long long #define INF 0x3f3f3f3f #define maxn 50010 #define eps 1e-6 #define mod 1000000007 using namespace std; LL dp[20][20][2000] ; int bit[30] ; LL dfs(int pos ,int v,int val,bool end1) { if(pos==-1) { return (val==0) ; } if(!end1&&dp[pos][v][val] != -1) { return dp[pos][v][val] ; } if(val<0) return 0 ; int e ,ee ; LL ans=0; if(end1) e = bit[pos] ; else e = 9 ; for(int i = 0 ; i <= e ;i++) { ee = val+(pos-v)*i ; ans += dfs(pos-1,v,ee,(end1&&i==e)) ; } if(!end1) { dp[pos][v][val] = ans; } return ans; } LL getans(LL n ) { int len=0; if(n<0) return 0 ; while(n) { bit[len++] = n%10 ; n /= 10 ; } LL ans=0; for( int i = 0 ; i <len ;i++) { ans += dfs(len-1,i,0,1) ; } return ans-(len-1) ; } int main() { int i , j , m ,k ,T ; LL x,y ; memset(dp,-1,sizeof(dp)) ; cin >> T ; while(T--) { cin >> x >> y ; y=getans(y) ; x=getans(x-1); cout << y-x<< endl; } return 0 ; }