• F(x)


    F(x)

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

    参考博客:http://www.cnblogs.com/zinthos/p/3899606.html

    数位dp

    这题用艾神教的数位dp方法好像过不了,于是我去网上找了下数位dp的做法(orz艾神自创算法)

     1 对于一个数,若其首位已经比询问上界小,则剩余位没有任何限制。
     2 此时如果能直接处理这一情况,则问题距离解决又会迈出一大步。
     3 例如,在十进制下,计算[10000,54321]内的数字和,
     4 我们可以将其分解为:
     5 [10000,19999],[20000,29999],[30000,39999],
     6 [40000,49999],[50000,54321]。
     7 前四个区间如果可以直接得到,则只需处理最后一个区间,
     8 进一步将最后一个区间划分为:
     9 [50000,50999],[51000,51999],[52000,52999],
    10 [53000,53999],[54000,54321]。
    11 同理将最后一个区间划分下去,最后可以得到以下区间划分:
    12 [10000,19999],[20000,29999],[30000,39999],
    13 [40000,49999],[50000,50999],[51000,51999],
    14 [52000,52999],[53000,53999],[54000,54099],
    15 [54100,54199],[54200,54299],[54300,54309],
    16 [54310,54319],[54320,54321]
    17 于是,我们只需要预处理出[10000,54321]的区间的值,
    18 再暴力[54320,54321]区间,那么即可得到结果。
    19 
    20 这个过程只需要用到记忆化搜索。
    21 
    22 数位DP的解题思路
    数位DP解题思路

    于是,数位dp就有了固定的解题模板:

     1 typedef long long LL;
     2 const int maxn=22;
     3 int dig[maxn];
     4 LL f[maxn]/* [TODO] */;
     5 
     6 LL dfs(int pos,/* TODO */,int limit){
     7     if (pos<0) return /* TODO */;
     8     if (!limit&&f[pos]/* [TODO] */!=-1) return f[pos]/* [TODO] */;
     9     LL res=0;
    10     int last=limit?dig[pos]:9;
    11     for (int i=0;i<=last;i++){
    12         res+=dfs(pos-1,/* TODO */,limit&&(i==last));
    13     }
    14     if (!limit) f[pos]/* [TODO] */=res;
    15     return res;
    16 }
    17 
    18 LL solve(LL n){
    19     int len=0;
    20     while (n){
    21         dig[len++]=n%10;
    22         n/=10;
    23     }
    24     return dfs(len-1,/* TODO */,1);
    25 }
    数位DP模板

    对于这道题来说,当前位上的数字对下一个状态来说是无关紧要的,重要的是填了这个数后剩下还能填多少值,

    于是可以定义状态:dp[idx][res]表示当前为第idx位剩余数值为res的情况数。

    当idx<0&&res>=0时,该状态合法;

    当res<0时,状态非法。

    代码如下:

     1 /*苟利国家生死已,岂因祸福避趋之*/
     2 #include<cstdio>
     3 #include<cstring>
     4 #define I 10         //位数
     5 #define RES 4600 //rest
     6 using namespace std;
     7 int T,A,B,dp[I][RES],ans,dig[I],bit[I];
     8 int dfs(int idx,int res,bool flag){
     9     if(idx<0)return res>=0;
    10     if(res<0)return 0;
    11     if(!flag&&dp[idx][res]!=-1)return dp[idx][res];
    12     int temp=0,last=flag?dig[idx]:9;
    13     for(int i=0;i<=last;++i)
    14         temp+=dfs(idx-1,res-i*bit[idx],flag&&i==last);
    15     if(!flag)dp[idx][res]=temp;
    16     return temp;
    17 }
    18 int solve(){
    19     int n=-1,res=0;
    20     while(B){
    21         dig[++n]=B%10;
    22         B/=10;
    23     }
    24     for(int i=0;A;A/=10,++i)
    25         res+=bit[i]*(A%10);
    26     return dfs(n,res,1);
    27 }
    28 void init(){
    29     memset(dp,-1,sizeof(dp));
    30     for(int i=0;i<10;++i)
    31         bit[i]=1<<i;
    32 }
    33 int main(void){
    34     init();
    35     scanf("%d",&T);
    36     for(int times=1;times<=T;++times){
    37         scanf("%d%d",&A,&B);
    38         ans=solve();
    39         printf("Case #%d: %d
    ",times,ans);
    40     }
    41 }

     总结:

    艾神的算法是给所有参数都创建一个维度,直接递推得到答案,当询问条件改变时,无法使用之前得到的值。适合询问次数少的情况。

    而记忆化搜索是预处理各个区间的值,最后暴力小区间,适用于询问次数较多的情况。

  • 相关阅读:
    梯度方向问题
    switchsharp
    R语言学习笔记:sort、rank、order、arrange排序函数
    R语言学习笔记:choose、factorial、combn排列组合函数
    MySQL学习笔记:少用Null
    Oracle学习笔记:11g服务介绍及哪些服务必须开启?
    GreenPlum学习笔记:create or replace function创建函数
    Python学习笔记:出生日期转化为年龄
    Python学习笔记:import sys模块(argv、path、platform、exit)
    Oracle学习笔记:wm_concat函数合并字段
  • 原文地址:https://www.cnblogs.com/barrier/p/6002535.html
Copyright © 2020-2023  润新知