HDU 3652:B-number(数位DP)

亮点:

收获:降一些时间复杂度可能带来编程复杂度。如果编程复杂度太高,而且时间允许的话,试着用一些冗余的状态来降低编程复杂度。

查询和正常DP的差别就在于:查询的时候有限制(最大不能超过那个数),而正常的时候没有

类型:数位DP

题意:找1~n内有多少能被13整除且含“13”这个子串的数。

方法:

(我的,想的比较乱的方法)

dp[i][d][mod]  表示d开头的i位数中含有“13”这个子串且%13==mod的数的个数

则:

那么下一个余数nextMod = ((mod-j*10i-1)%13+13)%13

dp[i][d][mod]  =  

                          1) d != 1:  ∑dp[i-1][j=(0~9)][nextMod]

                          2) d == 1:  ∑dp[i-1][j=(0~2,4~9)][nextMod] + ((10i-2 - nextnextMod-1/*-1去掉0*/)/13+1/*+1补上被去掉的那个*/)(接下来10i-1个数里,%13 == nextnextMod 的数。nextnextMod = ((next-3*10i-1)%13+13)%13)

但是,查询的时候,如果是1301,按照上面差的话,实际上只有1300和1301两个数,但是我们会算1300~1399这些数。 所以这里也不一样!。

唉,这种方法,d==1的情况太复杂,陷阱很多。+1-1的陷阱,然后查询的时候有陷阱。总之是一个很小心才能做出来的方法。

坑:

查询和正常DP的差别就在于:查询的时候有限制(最大不能超过那个数),而正常的时候没有。所以每一步都要小心,把查询和正常DP分开处理。这样才能对。另外,如果能更清晰一点,就不要这么写。。太容易错了。

 

方法二:(网上)

 

¨for x = 0 ~ 9
¨  if k = 1 //要求要包含13
¨    f[i,j,k,l] = f[i - 1,x,1,(l - j*10^(i-1))%13];
¨    if j = 1 and x = 3 //已经有13了。
¨      f[i,j,k,l] = f[i,j,k,l] +
¨                   f[i - 1,x,0,(l - j*10^(i-1))%13];
¨  else //不要求包含13
¨    if not (j = 1 and x = 3)
¨      f[i,j,k,l] = f[i - 1,x,0,(l - j*10^(i-1))%13];

这个方法多设了一维(包含和不包含),但清晰很多~

这就是我降维降出的编程复杂度…………T  T

 

收获:降一些时间复杂度可能带来编程复杂度。如果编程复杂度太高,而且时间允许的话,试着用一些冗余的状态来降低编程复杂度。

 

#include <cstdio>
#include <cstring>

int dp[20][10][13];
int num[20];
int n;

int dfs(int i, int d, int mod, bool isQuery) {
    if (!isQuery && dp[i][d][mod] != -1) {
        return dp[i][d][mod];
    }
    if (i == 1) {
        return dp[i][d][mod] = 0;
    }
    int end = isQuery?num[i-1]:9;
    int ans = 0;
    int ten = 1;
    for (int j = 0; j < i-1; j++) ten *= 10;
    int nextMod = ((mod-d*ten)%13 + 13)%13;
    for (int j = 0; j <= end; j++) {
        if (d == 1 && j == 3) {
            int nnxtMod = ((nextMod-(ten/10)*j)%13+13)%13;
            int thenum = (isQuery && j==end) ?(n%(ten/10)+1):(ten/10);
            int lala = thenum - nnxtMod - 1;
            if (lala >= 0) ans += lala/13+1;
            continue;
        }
        ans += dfs(i-1,j,nextMod,isQuery && j==end);
    }
    if (!isQuery) dp[i][d][mod] = ans;
    return ans;
}

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

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

 

 

 

posted on 2014-03-11 22:01  ShineCheng  阅读(249)  评论(0编辑  收藏  举报

导航