zoukankan      html  css  js  c++  java
  • 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 }

     总结:

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

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

  • 相关阅读:
    Windows性能计数器应用
    Azure Oracle Linux VNC 配置
    Azure 配置管理系列 Oracle Linux (PART6)
    Azure 配置管理系列 Oracle Linux (PART5)
    Azure 配置管理系列 Oracle Linux (PART4)
    Azure 配置管理系列 Oracle Linux (PART3)
    Azure 配置管理系列 Oracle Linux (PART2)
    vagrant多节点配置
    docker基本操作
    LINUX开启允许对外访问的网络端口命令
  • 原文地址:https://www.cnblogs.com/barrier/p/6002535.html
Copyright © 2011-2022 走看看