当前位置:编程学习 > C/C++ >>

hdu 3709 Balanced Number (DP_数位DP)

题目大意: 题目先给出平衡数的概念:数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艹代码:
[cpp] 
#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++; 
    } www.zzzyk.com
 
 
    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)); 
    } 


作者:woshi250hua
补充:软件开发 , C++ ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,