zoukankan      html  css  js  c++  java
  • 数位dp作业

    dp东西实在太多,昨天开了个树形dp入门,还没入呢,今天就要写数位dp,也不知道这种学习状态对不对啊?

    A - 不要62

    题意:

    输入n到m内,符合条件的数的个数。条件:不含62和4。

    这里直接学习qkoqhh大佬的思路。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #include<iostream>
     5 #include<queue>
     6 #include<cmath>
     7 #define inc(i,l,r) for(int i=l;i<=r;i++)
     8 #define dec(i,l,r) for(int i=l;i>=r;i--)
     9 #define link(x) for(edge *j=h[x];j;j=j->next)
    10 #define eps 1e-8
    11 #define inf 1000000007
    12 #define mem(a) memset(a,0,sizeof(a))
    13 #define ll long long
    14 #define succ(x) (1<<x)
    15 #define lowbit(x) (x&(-x))
    16 #define sqr(x) ((x)*(x))
    17 #define mid (x+y>>1)
    18 #define NM 305
    19 #define nm 1000005
    20 #define pi 3.141592653
    21 using namespace std;
    22 int read(){
    23     int x=0,f=1;char ch=getchar();
    24     while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    25     while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    26     return f*x;
    27 }
    28 int d[10][10],_x,n,b[10];
    29 //f:高位与原位是否相同 
    30 int dfs(int n,bool f,int t){
    31     if(!n)return 1;
    32     if(!f&&d[n][t])return d[n][t];
    33     int ans=0,m=f?b[n]:9;
    34     for(int i=0;i<=m;i++)
    35         if(i!=4&&!(t==6&&i==2)){
    36             ans+=dfs(n-1,f&&i==m,i);
    37             //printf("%d %d:%d
    ",n,i,ans);
    38         }
    39     return d[n][t]=ans;
    40 }
    41 int solve(int x){
    42     mem(b);mem(d);
    43     for(n=0;x;x/=10)b[++n]=x%10;
    44     return dfs(n,true,0);
    45 }
    46 int main(){
    47     while(_x=read())printf("%d
    ",solve(read())-solve(_x-1));
    48     return 0;
    49 }
    View Code

    下面贴上我学习这份优秀代码的一点注释

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 using namespace std;
     5 int bit[25], d[25][25];
     6 int dfs(int pos, bool flag, int t){
     7 /*
     8  * pos:当前位的位置;
     9  * flag:高位与原位相同(true)
    10  * t:高位,即当前位的前一位 
    11 */
    12     if(!pos)    return 1;
    13     if(!flag&&d[pos][t])    return d[pos][t];
    14     //高位与原位不同, 直接返回,不用考虑当前位与原位大小
    15     int ans = 0, m = flag?bit[pos]:9;
    16     for(int i=0;i<=m;i++){
    17         if(i!=4 && !(i==2&&t==6)){
    18             ans+=dfs(pos-1, flag&&i==m, i);
    19             //这里第二个参数可依据flag的定义理解
    20             //同样这里比较灵活,有时需要分类讨论 
    21         }
    22     }
    23     return d[pos][t]=ans;
    24 }
    25 int solve(int x){
    26     memset(bit, 0, sizeof(bit));
    27     memset(d, 0, sizeof(d));
    28     int len = 0;
    29     while(x){
    30         bit[++len] = x%10;
    31         x /= 10;
    32     }
    33     return dfs(len, true, 0);
    34 }
    35 int main(){
    36     int n, m;
    37     while( scanf("%d%d", &n, &m)!=EOF && n && m){
    38         printf("%d
    ", solve(m)-solve(n-1));
    39     }
    40     return 0;
    41 }

    B - B-number

    题意: 统计1到n中有13且能被13整除的数。

    这是我写的第二道数位dp题,目前对我来说,数位dp题的难点在于状态的转移合理正确的统计(这个比较灵活)。

    拿到这道题,思考后对比上题的模板,需要新增参数判断高位前是否出现了13,以及如何处理能被13整除。这里余数的处理用到了 同余定理乘法以及加法的运用,这个技巧我不熟练(不知道),这里余数的参数传递就有难度了。

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 using namespace std;
     5 int bit[25], d[25][25][13][2], c[25];
     6 bool vis[25][25][13][2];
     7 int dfs(int pos, bool flag, bool is13, int t, int mod){
     8 /*
     9  * pos:当前位的位置;
    10  * flag:高位与原位相同(true)
    11  * is13:高位前是否出现13 
    12  * t:高位,即当前位的前一位 
    13  * mod:当前数/13的余数 
    14 */
    15     if(!pos){
    16         if(is13 && !mod) return 1;  //出现13,且mod为0,可以整除
    17         else    return 0; 
    18     }
    19     if(!flag&&vis[pos][t][mod][is13])    return d[pos][t][mod][is13];
    20     int ans = 0, m = flag?bit[pos]:9;
    21     for(int i=0;i<=m;i++){
    22         if((t==1 && i==3) || is13){
    23             /*
    24              *int chushu = (mod*10 + i)%13;
    25              *这里不能这样算,有种自己在胡搞的感觉 
    26              *
    27              * 这里是同余定理加法和乘法的应用,其实不用深究,写个例子就明白了 
    28              * 可以这样理解 :
    29              * x%13=(c[i]*10^i + c[j]*10^j + ……)%13 = (mod + c[j]*10^j + ……)%13 
    30              * 拆开后就好
    31              * 这里可以预处理c[]
    32              * 传递时仔细对待mod
    33             */
    34             int chushu = (mod + c[pos]*i)%13;
    35             ans += dfs(pos-1, flag&&i==m, true, i, chushu);
    36         }else{
    37             int chushu = (mod + c[pos]*i)%13;
    38             ans += dfs(pos-1, flag&&i==m, false, i, chushu);
    39         }    
    40     }
    41     vis[pos][t][mod][is13]=true;
    42     return d[pos][t][mod][is13]=ans;
    43 }
    44 int solve(int x){
    45     memset(bit, 0, sizeof(bit));
    46     memset(d, 0, sizeof(d));
    47     memset(vis, 0, sizeof(vis)); 
    48     int len = 0;
    49     while(x){
    50         bit[++len] = x%10;
    51         x /= 10;
    52     }
    53     return dfs(len, true, false, 0, 0);
    54 }
    55 int main(){
    56     int n;
    57     c[1] = 1;
    58     for(int i=2; i<=10; i++) c[i]=c[i-1]*10%13;
    59     while( scanf("%d", &n)!=EOF && n ){
    60         printf("%d
    ", solve(n));
    61     }
    62     return 0;
    63 }

    C - Balance Number

    题意:

    定义一种平衡数:在pivot两边的数与pivot的torques之和相等,统计[x,y]内bn的个数。

    这里记录下做这题的思路(虽然这题写出来没过样例,但感觉自己思路还说的过去):

    先思考状态转移所需要的参数,即维度:除前面模板里套路的pos和flag外,还需要pivot的位置,还需要计算和比较pivot两边的torques之和是否相等。

    前三个维度各设一个参数,最后的比较可以设置为一个torqueses,对左右扭矩求向量和,若符合条件,则最终和为0(这里我没想到)。

    下来就是上面那个模板的套路去写。有几个需要注意的细节(其实都是我开始没想到的,对大佬来说这些很显然的东西,对我就从没显然过):

    1. 若扭矩和<0,直接返回,这里相当于利用一个剪枝,难思考但是不难理解。
    2. 就是在0的情况下,这种数无论pivot在哪都符合条件所以多级算了(len-1)次。dfs()搜索是0的情况是len位全0。
    3. 就是这道题有个记忆化搜索的如何记忆化的问题。什么时候更新dp,时间上还是有差距的,下面的代码在这点上与上面的模板不同。
     1 #include <cstdio>
     2 #include <algorithm>
     3 #include <cstring>
     4 using namespace std;
     5 #define LL long long
     6 const int INF = 0x3f3f3f3f;
     7 LL dp[20][20][2500];
     8 int bit[20];
     9 /*
    10  * pos:当前位位置
    11  * pivot: 题中所描述的balanced number的"中间"的那个点
    12  * torques:这里判断pivot左右扭矩之和是否相等可以用求其和torques看是否等于0
    13  *          这里最开始我的想法是用两个参数,扫描到pos=0时判断其是否相等,这样就烦琐了 
    14  * flag:同理前面的模板 
    15 */
    16 LL dfs(int pos, int pivot, int torques, bool flag){
    17     if(!pos)    return torques==0;
    18     //若枚举结束,扭矩和为0,则说明pivot这点可以使这个数成为balance number
    19     if(torques < 0)    return 0;
    20     //这里如果没有搜索完,扭矩和就小于0,则这个数一定不会是b n,这个判断很巧妙,这个有了方向后计算很方便 
    21     if(!flag && dp[pos][pivot][torques]!=-1) return dp[pos][pivot][torques];    
    22     LL ans = 0;
    23     LL m = flag?bit[pos]:9;
    24     for(int i=0; i<=m; i++){
    25         ans += dfs(pos-1, pivot, torques+i*(pos-pivot), flag&&i==m);
    26     }
    27     if(!flag)  dp[pos][pivot][torques]=ans;
    28     /*
    29      *  1、这里相对前面的模板不同。 这里记忆化对我来说又得注释一下, 
    30      *     这里高位与低位相同时, 
    31      *     当前pos是不能记忆的因为这里只枚举了0-bit[i]位,不符合总的dp[][][]状态的定义
    32      *     pos位,pivot点,扭矩和为torques的所有数 的个数
    33      *  2、同样也可以按前面的模板来写,不过需要对每个数的dp进行memset()。
    34      *  
    35      * 对比下二者的时间,156 : 249 
    36     */ 
    37     return ans;
    38 }
    39 LL solve(LL x){
    40     if(x < 0)    return 0;
    41     memset(bit, 0, sizeof(bit));
    42     int len = 0;
    43     while(x){
    44         bit[++len] = x%10;
    45         x /= 10;
    46     }
    47     LL ans = 0;
    48     for(int i=1; i<=len; i++){
    49         ans += dfs(len, i, 0, true);
    50     }
    51     return ans-(len-1);
    52     /*
    53      * 这里结果减去(len-1)我又没想到,在纸上画了画,
    54      * 发现在dfs过程中,重复计算了一种为0的情况,
    55      * 这个情况要筛去(len-1)个 
    56     */ 
    57 }
    58 int main(){
    59     int T;
    60     scanf("%d", &T);
    61     memset(dp, -1, sizeof(dp));
    62     while(T--){
    63         LL x, y;
    64         scanf("%lld%lld", &x, &y);
    65         printf("%lld
    ", solve(y)-solve(x-1));
    66     }
    67 }

    还有道C,改天补。

    这里想到了dp第一天韦神对A题有一种不同于上面那种解法的解法,因为代码看着比较复杂,一直没有仔细看,只是粗略的对比了二份代码的不同,发现那个思路比较注重对各种情况的全面讨论,难点在于对状态的抽象以及转移过程中的分类讨论,但是dp的维度是优于这个的,情况的表示也相对直观。这里先放上代码,改日细看。

     1 #include <stdio.h>
     2 #include <string.h>
     3 #include <string.h>
     4 #include <iostream>
     5 using namespace std;
     6 int dp[10][3];//dp[i][j],i代表数字的位数,j代表状况
     7 //dp[i][0],表示不存在不吉利数字
     8 //dp[i][1],表示不存在不吉利数字,且最高位为2
     9 //dp[i][2],表示存在不吉利数字
    10 void Init()
    11 {
    12     memset(dp,0,sizeof(dp));
    13     int i;
    14     dp[0][0] = 1;
    15     for(i = 1; i<=6; i++)//数字最长为6
    16     {
    17         dp[i][0] = dp[i-1][0]*9-dp[i-1][1];//最高位加上不含4的9个数字的状况,但因为会放6,所以要减去前一种开头为2的情况
    18         dp[i][1] = dp[i-1][0];//开头只放了2
    19         dp[i][2] = dp[i-1][2]*10+dp[i-1][0]+dp[i-1][1];//已经含有的前面放什么数都可以,或者是放一个4,或者是在2前面放6
    20     }
    21 }
    22 
    23 int solve(int n)
    24 {
    25     int i,len = 0,tem = n,ans,flag,a[10];
    26     while(n)//将每一位拆分放入数组
    27     {
    28         a[++len] = n%10;
    29         n/=10;
    30     }
    31     a[len+1] = ans = 0;
    32     flag = 0;
    33     int bb=0;
    34     for(i=len; i>=1; i--)
    35     {
    36         ans+=dp[i-1][2]*a[i];
    37         if(!flag && a[i]>4)//首位大于4,可以有放4的情况
    38             ans+=dp[i-1][0];
    39         if(!flag && a[i+1]==6 && a[i]>2)//后一位为6,此位大于2
    40             ans+=dp[i][1];
    41         if(!flag && a[i]>6)//此位大于6,可能的62状况
    42             ans+=dp[i-1][1];
    43         if(a[i]==4 || (a[i+1]==6&&a[i]==2)){
    44             bb=0;
    45             for(int j=i-1;j>=1;j--){
    46                 bb=bb*10+a[j];
    47             }
    48             break;
    49         }
    50     }
    51    return tem-ans-bb;
    52 }
    53 
    54 int main()
    55 {
    56     int l,r;
    57     Init();
    58     while(~scanf("%d%d",&l,&r),l+r)
    59     {
    60     // solve(4555);
    61         printf("%d
    ",solve(r+1)-solve(l));
    62         //因为solve函数中并没有考虑n是不是不幸数的情况,所以r+1只算了1~r,而l只算了1~l-1,这两者相减才是正确答案
    63    }
    64 
    65     return 0;
    66 }
    View Code
  • 相关阅读:
    day⑥:logging模块
    day⑥:shelve模块
    day⑥:xml模块
    day⑤:冒泡排序
    day⑤:模块
    day⑤:re深入
    day④:递归
    day④:迭代器
    day④:装饰器
    day③:函数式编程
  • 原文地址:https://www.cnblogs.com/seaupnice/p/9479082.html
Copyright © 2011-2022 走看看