现在的位置: 首页 > 综合 > 正文

数位DP练习

2014年01月03日 ⁄ 综合 ⁄ 共 5034字 ⁄ 字号 评论关闭

ural 1057 Amount of Degrees (数位DP)

详见:刘聪的论文,写的很详细......

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;

int f[35][35]; //前 i 位有 j 个 1 的个数...
int x,y,b;
int pos[35];
int len;
void init() { //预处理
    memset(f,0,sizeof(f));
    f[0][0] = 1;
    for(int i=1; i<=32; i++) {
        f[i][0] = f[i-1][0];
        for(int j=1; j<=i; j++) {
            f[i][j] = f[i-1][j-1] + f[i-1][j];
        }
    }
}

int solve(int t,int k) {
    len = 1;
    while(t) {
        pos[len++] = t % b;
        t /= b;
    }
    int ans = 0;
    for(int i=len-1; i>=1; i--) {
        if(pos[i] > 1) {
            ans += f[i-1][k] + f[i-1][k-1];
            break;
        } else if(pos[i] == 1) {
            ans += f[i-1][k];
            k --;
        }
        if(k < 0) break;
    }
    return ans;
}

int main() {
    init();
    int k;
    while(scanf("%d%d%d%d",&x,&y,&k,&b) != EOF) {
        printf("%d\n",solve(y+1,k) - solve(x,k));
    }
    return 0;
}

hdu 2089 不要62

题意:找出【x,y】区间内,满足不存在4且不存在62的数的个数

先预处理出前i位,j作为最高位时符合要求的数的个数。然后对于0--n的总个数,从高位到低位,枚举小于n的每一位。

#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
using namespace std;

int f[11][11];
int n,m;
int pos[11];
void init() {
    memset(f,0,sizeof(f));
    f[0][0] = 1;
    for(int i=1; i<7; i++) {
        for(int j=0; j<=9; j++) {
            if(j == 4) continue;
            for(int k=0; k<=9; k++) {
                if(j == 6 && k == 2) continue;
                f[i][j] += f[i-1][k];
            }
        }
    }
}

int solve(int x) {
    memset(pos,0,sizeof(pos));
    int len = 0;
    int flag = 1;
    while(x) {
        pos[++len] = x % 10;
        if(pos[len] == 4 || (pos[len] == 6 && pos[len-1] == 2)) flag = 0;
        x /= 10;
    }
    int ans = 0;
    if(flag) ans ++;
    for(int i=len; i>=1; i--) {
        for(int j=0; j<pos[i]; j++) {
            if(j == 4) continue;
            if( i < len && pos[i+1] == 6 && j == 2) continue;
            ans += f[i][j];
        }
        if(pos[i] == 4 || (i < len && pos[i+1] == 6 && pos[i] == 2)) break;
    }
    return ans;
}

int main() {
    init();
    while(scanf("%d%d",&n,&m)) {
        if(n == 0 && m == 0) break;
        printf("%d\n",solve(m) - solve(n-1));
    }
    return 0;
}

递归写法:

int dp[10][3];
int pos[10];

int dfs(int p,int buff,int flag) {
    if(p == -1) return (buff == 0 || buff == 1);
    if(flag == 0 && dp[p][buff] != -1) return dp[p][buff];
    int ans = 0;
    int end = flag ? pos[p] : 9;
    for(int i=0; i<=end; i++) {
        if(i == 4 || buff == 2 || (i == 2 && buff == 1)) ans += dfs(p-1,2,(flag && i == end));
        else if(i == 6) ans += dfs(p-1,1,(flag && i == end));
        else ans += dfs(p-1,0,(flag && i == end));
    }
    if(!flag) dp[p][buff] = ans;
    return ans;
}

int solve(int x) {
    int len = 0;
    while(x) {
        pos[len++] = x % 10;
        x /= 10;
    }
    return dfs(len - 1,0,1);
}

int main() {
    memset(dp,-1,sizeof(dp));
    int n,m;
    while(scanf("%d%d",&n,&m) && (n + m)) {
        printf("%d\n",solve(m) - solve(n-1));
        //printf("%d\n",m - solve(m) - (n - 1 - solve(n - 1)));
    }
    return 0;
}

hdu 3652 B-number

此题求的是存在连续的‘13’且能被13整除的数的个数

试试递归写法...记忆化dp

#include <iostream>
#include <algorithm>
#include <cmath>
#include <functional>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <climits>//形如INT_MAX一类的
using namespace std;

int dp[15][15][15][2];
int pos[15];

int dfs(int p,int pre,int mod,int buff,bool flag) {
    // p 表示当前处理位置, pre 表示前驱数字, buff 为目标状态(此题包括mod == 0), flag 表示此位置是否能枚举任意数字
    if(p == -1) return (buff && !mod);
    if(flag == 0 && dp[p][pre][mod][buff] != -1) return dp[p][pre][mod][buff];
    int ans = 0;
    int end = flag ? pos[p] : 9;
    for(int i=0; i<=end; i++) {
        if(pre == 1 && i == 3) ans += dfs(p - 1,i,(mod * 10 + i) % 13,1,(flag && i == end));
        else ans += dfs(p - 1,i,(mod * 10 + i) % 13,buff,(flag && i == end));
    }
    if(!flag) dp[p][pre][mod][buff] = ans;
    return ans;
}

int solve(int n) {
    int len = 0;
    while(n) {
        pos[len++] = n % 10;
        n /= 10;
    }
    return dfs(len - 1,0,0,0,1);
}

int main() {
    int n;
    memset(dp,-1,sizeof(dp));
    while(scanf("%d",&n) != EOF) {
        printf("%d\n",solve(n));
    }
    return 0;
}

poj 3252 Round Numbers

在区间【l,r】内的数当二进制表示时,求出满足条件的个数,条件为:0的个数大于等于1的个数。

需要考虑前导0

#include <iostream>
#include <algorithm>
#include <cmath>
#include<functional>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <queue>
#include <stack>
using namespace std;

int pos[40];
int dp[40][40][40];
int len ;
int dfs(int p,int num0,int num,int flag,int first) {
    if(p == -1) {
        if(num0 >= num) return 1;
        return 0;
    }
    if(flag == 0 && dp[p][num0][num] != -1) return dp[p][num0][num];
    int ans = 0;
    int end = flag ? pos[p] : 1;
    for(int i=0; i<=end; i++) {
        if(first) {
            if(i == 0) ans += dfs(p-1,num0,num,(flag && i == end),1);
            if(i == 1) ans += dfs(p-1,num0,num + 1,(flag && i == end),0);
        } else {
            if(i == 0) ans += dfs(p-1,num0 + 1,num,(flag && i == end),0);
            if(i == 1) ans += dfs(p-1,num0,num + 1,(flag && i == end),0);
        }

    }
    if(!flag) dp[p][num0][num] = ans;
    return ans;
}

int solve(int x) {
    len = 0;
    while(x) {
        pos[len ++] = x % 2;
        x /= 2;
    }
    return dfs(len - 1,0,0,1,1);
}

int main() {
    int n,m;
    memset(dp,-1,sizeof(dp));
//    while(scanf("%d",&n) != EOF) {
//        printf("%d\n",solve(n));
//    }
    scanf("%d%d",&n,&m);
    printf("%d\n",solve(m) - solve(n-1));
    return 0;
}

hdu 3709 balanced number

在[x,y]区间内求满足条件的数,条件是该数以某一位为轴心,其他位的权值为数位值 * 偏移量 ,要求轴心左边与右边权值和相等。

#include <iostream>
#include <algorithm>
#include <cmath>
#include<functional>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <climits>//形如INT_MAX一类的
#define MAX 100005
#define INF 0x7FFFFFFF
#define REP(i,s,t) for(int i=(s);i<=(t);++i)
#define ll __int64
using namespace std;

int pos[20];
ll dp[20][20][2222];

ll dfs(int p,int mid,int sum,int flag) {
    if(p == -1) return sum == 0;
    if(sum < 0) return 0;
    if(flag == 0 && dp[p][mid][sum] != -1) return dp[p][mid][sum];
    int end = flag ? pos[p] : 9;
    ll ans = 0;
    for(int i=0; i<=end; i++) {
        ans += dfs(p - 1,mid,sum + (p - mid) * i, (flag && i == end));
    }
    if(!flag) return dp[p][mid][sum] = ans;
    return ans;
}

ll solve(ll x) {
    int len = 0;
    while(x) {
        pos[len++] = x % 10;
        x /= 10;
    }
    ll ans = 0;
    for(int i=0; i<len; i++) {
        ans += dfs(len-1,i,0,1);
    }
    return ans - len + 1; // 减去前导零的情况
}

int main() {
    memset(dp,-1,sizeof(dp));
    int T;
    scanf("%d",&T);
    ll n,m;
    while(T--) {
        scanf("%I64d%I64d",&n,&m);
        printf("%I64d\n",solve(m) - solve(n-1));
    }
    return 0;
}

抱歉!评论已关闭.