HDU 3652 数位DP

题目链接
题意:1 - n中有多少个数 能被13整除 而且包含【13】

数位dp

状态dp[i][j][k][f]
第i位为j 除以13的余数为k
f两个状态
0:没有包含13的个数
1:包含13的个数

状态转移方程:
f = 1,j != 1 : dp[i][j][k][f] = SUM{ dp[i - 1][p][ (k - (j * 10 ^ (i - 1) ) + 13 ) % 13][1] ( p = 0 ~ 9)

f = 1,j = 1 : dp[i][j][k][f] = SUM{ dp[i - 1][p][ (k - (j * 10 ^ (i - 1) ) + 13 ) % 13][1] ( p = 0 ~ 9) + dp[i - 1][3][ (k - (j * 10 ^ (i - 1) ) + 13 ) % 13][0]

f = 0 ,j = 1 dp[i][j][k][f] = SUM{ dp[i - 1][p][ (k - (j * 10 ^ (i - 1) ) + 13 ) % 13][0] ( p = 0 ~ 9) - dp[i - 1][3][ (k - (j * 10 ^ (i - 1) ) + 13 ) % 13][0]

f = 0,j != 1 dp[i][j][k][f] = SUM{ dp[i - 1][p][ (k - (j * 10 ^ (i - 1) ) + 13 ) % 13][0] ( p = 0 ~ 9)

构造解:
构造解时 形如一下的数 : ####%××××
(#)为已经确定的数
(%)为当前枚举的数
(X)为待枚举的数

设pre_mode 为 ####00000 除以13的余数

为了构造出 能被13整除的数 所有 后部分 对13取模应为 w =(13 - pre_mode) % 13
has_13 表示已确定的数中是否已经包含了13

has_13 = 0 此时我们要构造出有包含了13 且和已确定的数组合在一起能被13整除 的数 dp(i,j,w,1) 如果此时num[i + 1] = 1 且num[i] > 3 时我们可以从一个不含13的数的前面加上以确定部分 形成正确解 dp[i][3][w][0]

has_13 = 1 此时我们只要在未确定部分构造出能被一个和确定部分组合后能被13整除的数即可 SUM{ dp[i][p][w][0] + dp[i][p][w][1] } p = 0 ~ 9

最后: 作为一个菜鸡,1A了很开心

代码如下:

#include <cstdio>
#include <iostream>
#include <queue>
#include <vector>
#include <cstring>
#define sf scanf
#define pf printf
using namespace std;
int dp[15][10][13][2];
int LARGE[10];
int digit[15];
void init(){
    LARGE[1] = 1;
    for(int i = 2;i < 10;++i){
        LARGE[i] = LARGE[i - 1] * 10;
        LARGE[i] %= 13;
    }
    memset(dp,-1,sizeof(dp));
    memset(dp[0],0,sizeof(dp[0]));

    memset(dp[1],0,sizeof(dp[1]));
    for(int j = 0;j < 10;++j){
        dp[1][j][j % 13][0] = 1;
    }
}

int DPS(int i,int j,int k,bool f){          //第i位为j mod 13 为 k  有/没 包含13
    int& ans = dp[i][j][k][f];
    if(ans != -1) return ans;
    ans = 0;
    if(f){
        int w = (k - (j * LARGE[i]) % 13 + 13) % 13;
        for(int p = 0;p < 10;++p){
            ans += DPS(i - 1,p,w,1);
        }
        if(j == 1){
            ans += DPS(i - 1,3,w,0);
        }
    }
    else{
        int w = (k - (j * LARGE[i]) % 13 + 13) % 13;
        for(int p = 0;p < 10;++p){
            ans += DPS(i - 1,p,w,0);
        }
        if(j == 1){
            ans -= DPS(i - 1,3,w,0);
        }
    }
    return ans;
}

int get_digit(int n){
    for(int i = 1;n;++i){
        digit[i] = n % 10;
        n = n / 10;
        if(!n) return i;
    }
}

int main(){
    init();
    int n;
    while( ~sf("%d",&n) ){
        int ans = 0,len = get_digit(n);
        digit[len + 1] = 0;
        int pre_mod = 0;
        bool has_13 = false;
        for(int i = len;i;--i){
            for(int j = 0;j < digit[i];++j){
                if(has_13){
                    ans += DPS(i,j,(13 - pre_mod) % 13,1);
                    ans += DPS(i,j,(13 - pre_mod) % 13,0);
                }
                else{
                    ans += DPS(i,j,(13 - pre_mod) % 13,1);
                }
            }
            if(!has_13 && digit[i + 1] == 1 && digit[i] > 3){
                ans += DPS(i,3,(13 - pre_mod) % 13,0);
            }
            pre_mod = (pre_mod + digit[i] * LARGE[i] ) % 13;
            if(digit[i + 1] == 1 && digit[i] == 3) has_13 = 1;
        }
        if(has_13 && n % 13 == 0) ans++;
        pf("%d\n",ans);
    }
    return 0;
}

DFS版本:

代码:

#include <cstdio>
#include <iostream>
#include <queue>
#include <cstring>
#define sf scanf
#define pf printf
using namespace std;
typedef long long LL;

int dp[13][3][13];  //dp[i][st][prev] 第i位 前缀数字 模13为prev st      
                    //3种状态 
                    //0 前面没有13 i + 1位不是1  
                    //1 前面没有13 i + 1位是1
                    //2 前面有13
int digit[13];
int dfs(int i,int st,int prev,bool flag){
    if(i == 0) return !flag && st == 2 && prev == 0;
    if(!flag && ~dp[i][st][prev]) return dp[i][st][prev];
    int limit = flag ? digit[i] : 9;
    int ans = 0;
    for(int j = 0;j <= limit;++j){
        int nst;
        if(st == 0){
            if(j == 1){
                nst = 1;
            }
            else nst = 0;
        }
        else if(st == 1){
            if(j == 1){
                nst = 1;
            }
            else if(j == 3){
                nst = 2;
            }
            else nst = 0;
        }
        else nst = 2;
        ans += dfs(i - 1,nst,(prev * 10 + j) % 13,flag && j == limit);
    }
    if(!flag) dp[i][st][prev] = ans;
    return ans;
}

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

int main(){
    memset(dp,-1,sizeof(dp));
    int n;
    while(~sf("%d",&n)) pf("%d\n",solve(n + 1));
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值