哈尔滨工程大学预热赛---G:A hard problem(数位dp)

题目:

Now, we have a function f(x):
int f ( int x ) {
    if ( x == 0 ) return 0;
    return f ( x / 10 ) + x % 10;
}
For a given interval [A, B] (1 <= A <= B <= 10^9), calculate how many integer x that mod f(x) equal to 0.

分析:

题意就是求[A,B]区间内能被自己各个数位的数字之和整除的数的个数

(1)打表:

由于范围不是特别大,可以每隔100000个数打一个表,最多开1e4的数组,时间最多1e5 * 50 * 10

(2)数位dp

考虑dp怎样定义状态,由于数位之和会变化,也就是模数f(x)不确定,也就定义不了状态,因为a % x = a % kx % x,所以我们可以确定每一次的模数为[1,81]的最小公倍数,但这个数太大,开不了dp数组,只有考虑枚举模数,也就是每次确定它的数位之和

HDU---3709(类似的处理方法)

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
int t,l,r,dit[15];
int dp[15][82][82][82];
int dfs(int pos,int sum,int mod,int MOD,int limit){
    if(pos < 0) return mod == MOD && sum == 0 ;
    if(mod > MOD) return 0;
    if(!limit && ~dp[pos][mod][MOD][sum]) return dp[pos][mod][MOD][sum];
    int up = limit ? dit[pos] : 9,num = 0;
    for(int i = 0;i <= up; ++i){
        num += dfs(pos-1,(sum*10+i)%MOD,mod+i,MOD,limit && dit[pos]==i);
    }
    if(!limit) dp[pos][mod][MOD][sum] = num;
    return num;
}
int solve(int x){
    int len = 0;
    while(x){
        dit[len++] = x % 10;
        x /= 10;
    }
    int res = 0;
    for(int i = 1;i <= 81; ++i) res += dfs(len-1,0,0,i,1);
    return res;
}
int main(){
    memset(dp,-1,sizeof(dp));
    cin >> t;
    for(int Case = 1;Case <= t; ++Case){
        cin >> l >> r;
        printf("Case %d: %d\n",Case,solve(r) - solve(l-1));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41157137/article/details/89076487
今日推荐