LightOJ 1140: How Many Zeroes? (数位DP)

当前数位DP还不理解的点:

1:出口用i==0的方式

2:如何省略状态d(就是枚举下一个数的那个状态。当然枚举还是要的,怎么把空间省了)

 

总结:

1:此类DP,考虑转移的时候,应当同时考虑查询时候的情况。

2:考虑x在第i位之后,能遍历多少数字,其答案为(x%10i-1+1)

3:这里的记忆化搜索不太一样喔,出口一定要写在递归里,不然,查询状态下差到出口就会出错了~

 

类型:

数位DP

 

题意:

求[A,B]区间内的所有数,写下来之后,0的个数。(a,b 为 unsigned int)

 

思路:

我的笨拙暴力状态:

dp[i][d][okPre] 表示d开头的i位数,(okPre表示计算前导0的情况下,反之~),的0的个数。

那么。

dp[i][d][含] = dp[i-1][0~9(num[i-1])][含] + 10i-1(x%10i-1+1) * (d==0);

dp[i][d][不含] = dp[i-1][1~9(num[i-1])][含] + dp[i-1][0][d==0?不含:含] ;

出口:

dp[1][1~9][含] =dp[1][1~9][不含] = 0;

dp[1][0][不含] = dp[1][0][含] = 1;

当时确定出口的时候,不含的0应该是0还是1呢?不好确定,感觉是1,最后是通过试验确定的。

还是没有理解别人代码中 用 i==0 做出口 是怎么实现的。


我的代码:

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

long long nowx;
long long dp[20][10][2];
int num[30];


long long dfs(int i, int d, bool okPreZero, bool isQuery) {
    //printf("(%d,%d,%s,%s)\n", i, d, okPreZero?"T":"F", isQuery?"T":"F");
    long long &nowdp = dp[i][d][okPreZero];
    if (!isQuery && ~nowdp) return nowdp;
    if (i == 1) {
        if (d!=0) return nowdp = 0;
        else if (okPreZero) {
            return nowdp = 1;
        } else {
            return nowdp = 1;
        }
    }
    long long ans = 0;
    int end = isQuery?num[i-1]:9;
    for (int j = 0; j <= end; j++) {
        if (okPreZero) {
            ans += dfs(i-1,j,true,isQuery && j==end);
        } else {
            if (d == 0 && j == 0) {
                ans += dfs(i-1,j,false,isQuery && j==end);
            } else {
                ans += dfs(i-1, j, true, isQuery && j==end);
            }
        }
    }
    long long ten = 1;
    for (int j = 0; j < i-1; j++) ten*=10;
    if (d==0 && okPreZero) ans += (isQuery?((nowx%ten)+1):(ten));
    if (!isQuery) nowdp = ans;
    return ans;
}

long long cal(long long x) {
    nowx = x;
    if (x == -1) return 0;
    if (x == 0) return 1;
    int len = 0;
    while (x) {
        num[++len] = x%10;
        x/=10;
    }
    return dfs(len+1, 0, false, true);
}


int Nmain() {
    long long a;
    memset(dp, -1, sizeof(dp));
    while (cin>>a) {
        cout<<"---"<<cal(a)<<endl;
    }
    return 0;
}

int main() {
    int t;
    cin>>t;
    int cas = 1;
    memset(dp, -1, sizeof(dp));
    while (t--) {
        long long m, n;
        cin>>m>>n;
        cout<<"Case "<<cas++<<": "<<cal(n)-cal(m-1)<<endl;
    }
    return 0;
}

 

不理解的代码:

#include<cstdio>
#include<cstring>
#include<cmath>
typedef long long LL;
LL dp[14][12][2];
int bit[14],len;
LL a,b;
LL dfs(int pos,int v,int flag,int limit)
{
    if (pos<=0) return flag?v:0;
    if (!limit&&dp[pos][v][flag]!=-1) return dp[pos][v][flag];
    int end=(limit?bit[pos]:9);
    LL re=0;
    for (int i=0;i<=end;i++)
        {
            int tmp;
            if (flag&&(i==0)) tmp=1;else tmp=0;
            re+=dfs(pos-1,v+tmp,flag||i,limit&&(end==i));
        }
    if (!limit) dp[pos][v][flag]=re;
    return re;
}

LL solve(LL n)
{   if (n==-1) return -1;
    if (n==0) return 0;
    len=0;
    while (n)
        {   bit[++len]=n%10;
            n/=10;
        }
    return dfs(len,0,0,1);
}
int main()
{   memset(dp,255,sizeof(dp));
    int cas,i=0;
    scanf("%d",&cas);
    while (cas--)
        {scanf("%lld%lld",&a,&b);
         printf("Case %d: %lld\n",++i,solve(b)-solve(a-1));
        }
    return 0;
}

 

posted on 2014-03-13 21:52  ShineCheng  阅读(433)  评论(0编辑  收藏  举报

导航