数位dp

数位dpf[i][j] 表示i位数满足j的情况下的个数,然后用记忆化搜索的写法来解决转移和统计的问题。 

HDU3555 Bomb

题意:求1到n中含49的数的个数。

思路:·pos:当前位数。

   ·st:高位状态(st=0:前几位中没有49;st=1:前一位是4;st=2:前几位中有49)。

   ·flag:高位是否是原数。

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
int bit[50];
ll f[50][5];

ll dfs(int pos,int st,bool flag)
{
    //pos:当前位数
    //st:高位状态    st=0:没有49;st=1:前一位是4;st=2:前几位中有49
    //flag:高位是否是原数
    if(pos == 0) return st == 2;
    if(flag && f[pos][st] != -1) return f[pos][st];
    ll ans = 0;
    int up = flag ? 9 : bit[pos];
    for(int i = 0; i <= up; i++)
    {
        if(st == 2 || st == 1 && i == 9){
            ans += dfs(pos-1,2,flag || i < up);
        }
        else if(i == 4){
            ans += dfs(pos-1,1,flag || i < up);
        }
        else{
            ans += dfs(pos-1,0,flag || i < up);
        }
    }
    if(flag) f[pos][st] = ans;
    return ans;
}
ll solve(ll x)
{
    int len = 0;
    while(x){
        bit[++len] = x % 10;
        x /= 10;
    }
    return dfs(len,0,false);
}
int main()
{
    int t;
    memset(f,-1,sizeof(f));
    scanf("%d",&t);
    while(t--)
    {
        ll num;scanf("%lld",&num);
        printf("%lld\n",solve(num));
    }
    return 0;
}
View Code

 HDU3652 B-number

题意:求1到n中含有13且能被13整除的数有多少个。

思路:含有13的条件与上一题类似。对于整除13,利用同余把除以13的余数也计入状态,即多出一个mod的状态,dfsint dfs(int pos,int mod,int st,int flag)

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
int bit[20];
int f[20][5][15];

int dfs(int pos,int mod,int st,bool flag)
{
    if(pos == 0 ) return st == 2 && mod == 0;
    if(flag && f[pos][st][mod] != -1) return f[pos][st][mod];
    int ans = 0;
    int up = flag ? 9 : bit[pos];
    for(int i = 0; i <= up; i++)
    {
        int _mod = mod * 10 + i;
        _mod %= 13;
        if(st == 2 || st == 1 && i == 3){
            ans += dfs(pos-1,_mod,2,flag || i < up);
        }
        else if(i == 1){
            ans += dfs(pos-1,_mod,1,flag || i < up);
        }
        else{
            ans += dfs(pos-1,_mod,0,flag || i < up);
        }
    }
    if(flag) f[pos][st][mod] = ans;
    return ans;
}
int solve(int x)
{
    int len = 0;
    while(x){
        bit[++len] = x % 10;
        x /= 10;
    }
    return dfs(len,0,0,false);
}
int main()
{
    memset(f,-1,sizeof(f));
    int num;
    while(~scanf("%d",&num))
    {
        printf("%d\n",solve(num));
    }
    return 0;
}
View Code

 HDU4389 X mod f(x)

题意:计算区间内满足数字各位之和能整除该数字的整数个数。

思路:又和上一题有一点点像😅。由于各数位之和为1到81,所以可以对每一个数都进行一次操作就可以了。f[pos][mod][num][sum]表示前pos位数,除以num的余数为mod,各数位之和为sum的满足条件的数的个数。

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
int bit[20];
int f[10][85][85][85];

int dfs(int pos,int mod,int num,int sum,bool flag)
{
    if(pos == 0 ) return sum == num && mod % sum == 0;
    if(flag && f[pos][mod][num][sum] != -1) return f[pos][mod][num][sum];
    int ans = 0;
    int up = flag ? 9 : bit[pos];
    for(int i = 0; i <= up; i++)
    {
        int _mod = mod * 10 + i;
        _mod %= num;
        ans += dfs(pos-1,_mod,num,sum+i,flag || i <up);
    }
    if(flag) f[pos][mod][num][sum] = ans;
    return ans;
}
int solve(int x)
{
    int len = 0;
    while(x){
        bit[++len] = x % 10;
        x /= 10;
    }
    int ret = 0;
    for(int i=1;i<=81;i++)
    {
        ret += dfs(len,0,i,0,false);
    }
    return ret;
}
int main()
{
    memset(f,-1,sizeof(f));
    int t;scanf("%d",&t);
    for(int cas = 1; cas <= t; cas++)
    {
        int l,r;
        scanf("%d%d",&l,&r);
        int ans = solve(r) - solve(l-1);
        printf("Case %d: %d\n",cas,ans);
    }
    return 0;
}
View Code

 HDU2089 不要62

一道简单的数位dp

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
int a[20];
int dp[20][2];
int dfs(int pos,int pre,int sta,bool limit)
{
    if(pos==-1) return 1;
    if(!limit && dp[pos][sta]!=-1) return dp[pos][sta];
    int up=limit ? a[pos] : 9;
    int tmp=0;
    for(int i=0;i<=up;i++){
        if(pre==6 && i==2)continue;
        if(i==4) continue;
        tmp+=dfs(pos-1,i,i==6,limit && i==a[pos]);
    }
    if(!limit) dp[pos][sta]=tmp;
    return tmp;
}
int solve(int x)
{
    int pos=0;
    while(x){
        a[pos++]=x%10;
        x/=10;
    }
    return dfs(pos-1,-1,0,true);
}
int main()
{
    int le,ri;
    while(~scanf("%d%d",&le,&ri) && le+ri){
        memset(dp,-1,sizeof dp);
        printf("%d\n",solve(ri)-solve(le-1));
    }
    return 0;
}
View Code

 HDU3709 Balanced Number

题意:求区间内平衡数的个数。平衡数的定义为:这个数字的某一位为支点,另外两边的数字大小乘以力矩之和相等(对于每一个数位)。

思路:·枚举支点,计算力矩,f[pos][x][st]表示pos位的数,支点是x,目前的力矩为st的数的个数。

   ·转移:f[pos][x][st] = Σf[pos-1][x][st + i * (pos - x)]

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
int bit[25];
ll f[25][25][2005];

ll dfs(int pos,int x,int st,bool flag)
{
    if(pos == 0 ) return st == 0;
    if(flag && f[pos][x][st] != -1) return f[pos][x][st];
    ll ans = 0;
    int up = flag ? 9 : bit[pos];
    for(int i = 0; i <= up; i++)
    {
        ans += dfs(pos-1,x,st + i * (pos - x),flag || i < up);
    }
    if(flag) f[pos][x][st] = ans;
    return ans;
}
ll solve(ll x)
{
    int len = 0;
    while(x){
        bit[++len] = x % 10;
        x /= 10;
    }
    ll ret = 0;
    for(int i=1;i<=len;i++){
        ret += dfs(len,i,0,false);
    }
    return ret - len + 1;
}
int main()
{
    memset(f,-1,sizeof(f));
    int t;scanf("%d",&t);
    for(int cas = 1; cas <= t; cas++)
    {
        ll l,r;
        scanf("%lld%lld",&l,&r);
        ll ans = solve(r) - solve(l-1);
        printf("%lld\n",ans);
    }
    return 0;
}
View Code

 

posted on 2018-08-31 12:34  solvit  阅读(228)  评论(0)    收藏  举报

导航