• 数位DP入门题


    站点一览:

    1. hdu 2089"不要62"
    2. hdu 4734"F(X)"
    3. poj 3252"Round Numbers"
    4. hdu 3709"Balanced Number"

    1.hdu 2089"不要62"

    题解:

      题目过于简单,不再赘述。

    AC代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 using namespace std;
     5 #define mem(a,b) memset(a,b,sizeof(a))
     6 
     7 int a,b;
     8 int digit[10];
     9 int dp[10][2];//dp[i][0]:位置i的前一个位置不为6的总方案数;dp[i][1]正好相反 
    10 
    11 int DFS(int curPos,int preNum,int isSix,bool limit)
    12 {
    13     if(curPos == -1)
    14         return 1;
    15     if(!limit && dp[curPos][isSix] != -1)
    16         return dp[curPos][isSix];
    17         
    18     int up=limit ? digit[curPos]:9;
    19     int ans=0;
    20     for(int i=0;i <= up;++i)
    21     {
    22         if((preNum == 6 && i == 2) || i == 4)
    23             continue;
    24          ans += DFS(curPos-1,i,i == 6,limit&& i == digit[curPos]);
    25     }
    26     if(!limit)
    27         dp[curPos][isSix]=ans;
    28     return ans;
    29 }
    30 int Solve(int x)
    31 {
    32     int k=0;
    33     while(x)
    34     {
    35         digit[k++]=x%10;
    36         x /= 10;
    37     }
    38     return DFS(k-1,0,0,true);
    39 }
    40 
    41 int main()
    42 {
    43     while(~scanf("%d%d",&a,&b) && a+b)
    44     {
    45         mem(dp,-1);
    46         printf("%d
    ",Solve(b)-Solve(a-1));
    47     }
    48     return 0;
    49 }
    View Code

    2.hdu 4734"F(X)"

    Problem Description
    For a decimal number x with n digits (AnAn-1An-2 ... A2A1), we define its weight as F(x) = An * 2n-1 + An-1 * 2n-2 + ... + A2 * 2 + A1 * 1.
    Now you are given two numbers A and B, please calculate how many numbers are there between 0 and B, inclusive, whose weight is no more than F(A).
    
    Input
    The first line has a number T (T <= 10000) , indicating the number of test cases.
    For each test case, there are two numbers A and B (0 <= A,B < 109)
    
    Output
    For every case,you should output "Case #t: " at first, without quotes. The t is the case number starting from 1. Then output the answer.

    题解:

      初始想法:

      定义dp[ i ][ j ] : [ 0,i ] 位置满足 weight == j 的数的总个数;

      求出F(a)后,便利 i : 0~F(a) ,求出 [0,b] weight == i 的数的总个数,作加和;

      很不幸,TLE,不过,还是挺开心的,毕竟是在看题解前按照自己的想法成功敲出的代码,虽然TLE了;

      正解:定义dp[ i ][ j ] : [ 0,i ]位置满足 weight <= j 的数的总个数,然后,每次判断 dp[ i ][ j ]是否可以直接返回;

    AC代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 using namespace std;
     5 #define mem(a,b) memset(a,b,sizeof(a))
     6 
     7 int a,b;
     8 int power[10]={1,2,4,8,16,32,64,128,256};
     9 int digit[15];
    10 int dp[15][30000];//dp[i][j]:前i位F值小于等于j的总个数
    11 
    12 int F(int x)
    13 {
    14     int sum=0;
    15     int base=1;
    16     while(x)
    17     {
    18         sum += (x%10)*base;
    19         base <<= 1;
    20         x /= 10;
    21     }
    22     return sum;
    23 }
    24 int DFS(int need,int curPos,int curSum,bool limit)
    25 {
    26     if(curPos == -1)
    27         return curSum <= need ? 1:0;
    28         
    29     if(curSum > need)
    30         return 0;
    31         
    32     if(!limit&&dp[curPos][need-curSum] != -1)
    33         return dp[curPos][need-curSum];
    34 
    35     int up=limit ? digit[curPos]:9;
    36     int ans=0;
    37     for(int i=0;i <= up;++i)
    38         ans += DFS(need,curPos-1,curSum+i*power[curPos],limit&&i==digit[curPos]);
    39         
    40     if(!limit)
    41         dp[curPos][need-curSum]=ans;
    42 
    43     return ans;
    44 }
    45 int Solve(int x)
    46 {
    47     int k=0;
    48     while(x)
    49     {
    50         digit[k++]=x%10;
    51         x /= 10;
    52     }
    53     int f=F(a);
    54     return DFS(f,k-1,0,true);
    55 }
    56 int main()
    57 {
    58     int test;
    59     scanf("%d",&test);
    60     mem(dp,-1);
    61     for(int kase=1;kase <= test;++kase)
    62     {
    63         scanf("%d%d",&a,&b);
    64         printf("Case #%d: %d
    ",kase,Solve(b));
    65     }
    66     return 0;
    67 }
    View Code

    3.poj 3252"Round Numbers"

    题意:

      输入两个十进制正整数a和b,求闭区间 [a ,b] 内有多少个Round number

      所谓的Round Number就是把一个十进制数转换为一个无符号二进制数,若该二进制数中0的个数大于等于1的个数,则它就是一个Round Number

      注意,转换所得的二进制数,最高位必然是1,最高位的前面不允许有0

    题解:

      定义dp[ i ][ j ][ k ] : 在没有限制的情况下,[0,i]位置满足 0 的个数等于 j,1 的个数等于 k 的数的总个数;

      坑点:前导零不能算在 0 的总个数中;

    因为poj炸了,所以一直处于wait状态,不过对拍了一下他人的AC代码,正确率是可以保证的,应该也不会超时吧orz;

    wait代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 using namespace std;
     5 #define mem(a,b) memset(a,b,sizeof(a))
     6 
     7 int a,b;
     8 int digit[35];
     9 int dp[35][35][35];
    10 
    11 //isSat:只有当出现第一个不为0的位数时才为true
    12 //作用是去掉前导0的影响 
    13 int DFS(int curPos,int totZero,int totOne,bool isSat,bool limit)
    14 {
    15     if(curPos == -1)
    16         return totZero >= totOne;
    17         
    18     if(!limit && dp[curPos][totZero][totOne] != -1)
    19         return dp[curPos][totZero][totOne];
    20         
    21     int up=limit ? digit[curPos]:1;
    22     int ans=0;
    23     for(int i=0;i <= up;++i)
    24     {
    25         bool flag=(isSat || i != 0);
    26         ans += DFS(curPos-1,totZero+(flag&&i==0),totOne+(i==1),flag,limit&&i==digit[curPos]);
    27     }
    28     if(!limit)
    29         dp[curPos][totZero][totOne]=ans;
    30     return ans;
    31 }
    32 int Solve(int x)
    33 {
    34     int k=0;
    35     while(x)
    36     {
    37         digit[k++]=x%2;
    38         x >>= 1;
    39     }    
    40     return DFS(k-1,0,0,false,true);
    41 }
    42 int main()
    43 {
    44     mem(dp,-1);
    45     while(~scanf("%d%d",&a,&b))
    46         printf("%d
    ",Solve(b)-Solve(a-1));
    47         
    48     return 0;
    49 }
    View Code

    4.hdu 3709"Balanced Number"

    题意:

      给一个很大的区间[x,y],(0 ≤ x ≤ y ≤ 1018).问:区间里面的数满足如下规则的有多少个?

      规则:将数字放在天平上,天平能够平衡。天平的轴随意,力臂就是数字下标到天平轴的下标的距离。

    题解:

      脑海中浮现出如何记忆化搜索的代码,可就是没想到如何判断当前数是否为"balance number",无奈之下,查阅大佬代码orz;

      坑点:全为0的数会重复计算,需要减去重复的部分

    AC代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 using namespace std;
     5 #define ll long long
     6 #define mem(a,b) memset(a,b,sizeof(a))
     7 
     8 ll a,b;
     9 int digit[20];
    10 ll dp[20][20][1800];
    11 
    12 //curSum:记录curPivot左边的和+右边的和,只有curSum == 0才能说明
    13 // curPivot左边的和==右边的和 
    14 ll DFS(int curPos,int curPivot,int curSum,bool limit)
    15 {
    16     if(curPos == -1)
    17         return curSum == 0 ? 1:0;
    18     if(curSum < 0)
    19         return 0;
    20     if(!limit && dp[curPos][curPivot][curSum] != -1)
    21         return dp[curPos][curPivot][curSum];
    22         
    23     int up=limit ? digit[curPos]:9;
    24     ll ans=0;
    25     for(int i=0;i <= up;++i)
    26         ans += DFS(curPos-1,curPivot,curSum+i*(curPos-curPivot),limit&&i==digit[curPos]);
    27     
    28     if(!limit)
    29         dp[curPos][curPivot][curSum]=ans;
    30     return ans;
    31 }
    32 ll Solve(ll x)
    33 {
    34     if(x == -1)
    35         return 0;
    36     int k=0;
    37     while(x)
    38     {
    39         digit[k++]=x%10;
    40         x /= 10;
    41     }
    42     ll ans=0;
    43     for(int i=k-1;i >= 0;--i)
    44         ans += DFS(k-1,i,0,true);
    45     return ans-k+1;
    46     //0个0,1个0,.......,(k-1)个0,全为0的情况被记录了k次,须减去(k-1)个重复的 
    47 }
    48 int main()
    49 {
    50     int test;
    51     scanf("%d",&test);
    52     mem(dp,-1);
    53     while(test--)
    54     {
    55         scanf("%lld%lld",&a,&b);
    56         printf("%lld
    ",Solve(b)-Solve(a-1));
    57     }            
    58     return 0;
    59 }
    View Code
  • 相关阅读:
    【WPF】WPF的二维路径标记语法绘图PathGeometry
    【转】【WPF】WPF无边框窗体
    【转】【数学】矩阵求逆的几何意义
    【转】【数学】矩阵的旋转
    【转】【OPenGL】理解OpenGL拾取模式(OpenGL Picking)
    WPF DataGrid 表头单元格居中DataGridCheckBoxColumn居中单击选中
    Revit 调用自己的窗体,以及导出图纸图片
    OpenXml 学习记录 标签目录 插入文本 表格 图片
    【转】【OpenXML】OpenXML操作word
    【转】【OpenXml】OpenXml操作Word的一些操作总结
  • 原文地址:https://www.cnblogs.com/violet-acmer/p/10568181.html
Copyright © 2020-2023  润新知