hdu 3709 Balanced Number (DP_数位DP)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3709

题目大意: 题目先给出平衡数的概念:数n以数n中的某个位为支点,每个位上的数权值为(数字xi*(posi - 支点的posi)),如果数n里有一个支点使得所有数权值之和为0那么她就是平衡数。比如4139,以3为支点,左边 = 4 * (4 - 2) + 1 * (3  - 2) = 9,右边 = 9 * (1 - 2) = -9,左边加右边为0,所以4139是平衡数。现在给出一个区间[l,r],问区间内平衡数有多少个?

解题思路:第一道数位DP,之前一直不会做这类题目,今天花了两个小时看了几篇博客,略懂皮毛,也参照某神牛的代码敲了下这题。

    这类题目用逆推要比正推好做,方法是记忆化搜索。每次向下传递目前的状态,下面每次都返回通过这些状态后面能得到的结果。

    因为要权值之和为0,我们枚举每个支点o,然后从高位往地位搜索并记录状态,这里的状态为当前的位置pos,之前的权值之和pre、支点o,这三个组合起来就可以表示一个状态。每种状态都至多遍历一次,如果第二次遍历到某个状态,就直接返回前一次往下遍历的结果。

    上一段已经解释了Dfs中的三个参数,那还剩下一个参数limit是干什么的?limit表示是否有上界,如果我们要找的是[0,12345,现在找到123,这时limit还是1,如果下一个枚举到的数是3,limit就变成0,以后都可以枚举到9而不是到5.


测试数据:

2
0 9
7604 24324


C艹代码:

#include <stdio.h>
#include <string.h>
#define MAX 2000
#define int64 long long


int64 dp[20][20][MAX];                  //dp记忆化搜索用
int64 digit[20],left,right;             //left和right为输入的一大一小值


int64 Dfs(int64 pos,int o,int64 pre,int limit) {
    //pos表示当前位置,o表示支点,pre表示从最高位到pos的力矩之和,limit表示是否有上限 1有 0无
    if (pos == -1) return pre == 0;         //已经全部组合
    if (pre < 0) return 0;                  //前面组合而成的力矩之和已经小于0,后面的也都是负数
    if (!limit && dp[pos][o][pre] != -1)
        return dp[pos][o][pre];             //没有上限且当前的状态之前已经搜索过


    int64 sum = 0;
    int64 end = limit ? digit[pos] : 9;     //有上限就设为上限,否则最高到9
    for (int i = 0; i <= end; ++i) {

        int64 next = pre;                   //枚举下一个状态
        next += (pos - o) * i;
        sum += Dfs(pos-1,o,next,limit && i == end);
    }


    if (!limit) dp[pos][o][pre] = sum;
    return sum;
}
int64 Cal(int64 x) {
    
    int64 pos = 0,ans = 0;              //位数,总个数
    while (x) {
        
        digit[pos] = x % 10;
        x /= 10,pos++;
    }


    for (int o = 0; o < pos; ++o)       //枚举支点
        ans += Dfs(pos-1,o,0,1);
    return ans - pos + 1;               //去除全是0的情况
}


int main()
{
    int i,j,k,t;
    
    
    scanf("%d",&t);
    while (t--) {
        
        scanf("%lld%lld",&left,&right);
        memset(dp,-1,sizeof(dp));
        printf("%lld\n",Cal(right) - Cal(left-1));
    }
}


本文ZeroClock原创,但可以转载,因为我们是兄弟。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值