hdu 3652 -数位dp

8 篇文章 0 订阅

之前学校新生培训的时候学长挂的题,当时给了标准代码没看明白,放了半年,今天花一天总算看明白了,从这个过程也能看出这半年学习是有不少进步的,代码的理解有一定的加深,不过单看数位dp7k+那个缩减模版有的地方确实是有点丧心病狂了,所以参照了另一个注释写的比较多的博主http://blog.csdn.net/xingyeyongheng/article/details/8806414最后思路综合了下,写了一个符合自己感觉的写法,明天找机会继续练练数位dp。

#include<stdio.h>
#include<string.h>

const int max=10+5;

//由基础模版dp[max][3]衍变,max代表的是数的最大长度会是多少,3代表的三种状态; 
//这里由于还有一个要求是除了数要包含13以外还需要被13整除,那么就需要再增加一维来代表余数 
/*
假设pos为输入数的转化为对应数位后的最大下标:
  dp[i][j][0]表示从pos到i+1位不含有13且接下去长度<=i的模13为j的数的个数 
  dp[i][j][1]表示从pos到i+1位不含有13但是第i+1位是1且接下去长度<=i的模13为j的数的个数 
  dp[i][j][2]表示从pos到i+1位含有13且接下去长度<=i的模13为j的数的个数  
*/
                         
int dp[max][3][13]; 
int digit[max];//储存每一位的数 

//pos代表处理到的位置,s为状态,mod为当前总的余数,flag作为一个标记比如输入数为123 
//处理到3,如果前面我们取得'2'的那位要的是2 那么我们flag=0
int dfs(int pos,int s, int mod,int flag){//表示后面不可以直接从129开始遍历,反之如果小于2,那么就可以从119开始遍历
    if(pos==-1)return s==2&&mod==0;//如果所有位都考虑完了,就判断返回必须是状态2且总余数为0才符合要求                                    
    if(flag&&dp[pos][s][mod]!=-1)return dp[pos][s][mod];//表示pos后面的位数可以是从9-1的任意数了在有值得情况下可以直接返回 
    int sum=0;
    int size = flag?9:digit[pos];//当前数可不可以从9开始,得看flag标记的前一位数是不是取了它最大的情况 
    for(int i=0;i<=size;i++){
        int t;//记录状态转移 
        if(s==2)t=2;//原来就已经包含13 
        else{
             if(s==1&&i==3)t=2;//原来有1,现在有3那么刚好存在13
             else if(i==1)t=1;
             else t=0;
        }
        int MOD=(mod*10+i)%13;//传到下一位总余数也要传递一下
        sum+=dfs(pos-1,t,MOD,flag||i<size);//i<size保证没有取上一位的最大值,那么后面的位上的数就可以是任意值了 
    }
    if(flag)dp[pos][s][mod]=sum;//在pos以后为可以取任意数的情况下,这种状态是饱满的,做一个记忆储存,供后面查询使用 
    return sum; 
} 

int cal(int n){
   int len = 0;
   while(n){
       digit[len++] = n%10;//转化成对应的数位 
       n/=10;
   }         
   return dfs(len-1,0,0,0);//记忆化搜索 
} 

int main(){
   int n;
   memset(dp,-1,sizeof(dp));//这里初始化一次就好,每次算的信息还可以供以后用 
   while(scanf("%d",&n)!=EOF){
       printf("%d\n",cal(n));                      
   }
   return 0;
} 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值