hdu3652(数位DP)

做了n久才搞定,感觉自己的数位DP学的太不扎实了

题意:求1--n中能有13这个数字且能被13整除的数的数目

解释看代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#include<time.h>
#include<math.h>

#define inf 0x7fffffff
#define eps 1e-9
#define pi acos(-1.0)
#define P system("pause")
using namespace std;
int dp[15][15][3];/* 考虑是否含有13分为三种情况;1、不含13;2、不含13并且最高位是3;3、含有13
                      但是这次还要考虑能否被13整除,所以还要加一维dp[i][j][k],i表示位数,j表示取模13得到的余数 
                      k表示1、2、3这三种情况                                                                    
                  */                                                                           
int md[15];
void init()
{
     memset(md,0,sizeof(md));
     memset(dp,0,sizeof(dp));
     int i,j,k;
     md[0] = 1;
     for(i = 1; i < 10; i++)  md[i] = (md[i-1]*10) %13;//md表示1,10,100,1000........模13的余数 
     dp[0][0][0] = 1;
     
     for(i = 1; i < 12; i++)
           for(j = 0; j < 13; j++)
           {
                 for(k = 0; k < 10; k++)
                     dp[i][(md[i-1]*k+j)%13][0] += dp[i-1][j][0];
                 dp[i][(md[i-1]+j)%13][0] -= dp[i-1][j][1];
                 dp[i][(md[i-1]*3+j)%13][1] += dp[i-1][j][0];
                 for(k = 0; k < 10; k++)
                       dp[i][(md[i-1]*k+j)%13][2] += dp[i-1][j][2];
                 dp[i][(md[i-1]+j)%13][2] += dp[i-1][j][1]; 
           }        
}
int fun(int n)
{
    int i,a[15],len = 0;
    memset(a,0,sizeof(a));
    while(n)
    {
          a[len++] = n % 10;
          n /= 10;          
    }
    int ans = 0,mod = 0,k;
    bool flag = 0;
    for(i = len - 1; i >= 0;mod = (mod + md[i]*a[i])%13,i--)
    {
          for(k = 0; k < a[i]; k++) 
                 ans += dp[i][(13- (mod + k*md[i])%13)%13][2];//i-1位中存在13的情况 
          if(flag)                                //前i+1位中已经出现了13 
             for(k = 0; k < a[i]; k++)
                  ans += dp[i][(13- (mod + k*md[i])%13)%13][0];
          if(!flag && a[i] > 3 && a[i+1] == 1)    //前一位和当前位构成13的情况
                   ans += dp[i+1][(13- mod)%13][1];
          if(!flag && a[i] > 1)//当前位和后一位构成13的情况
                   ans += dp[i][(13- (mod + md[i])%13)%13][1];
          if(a[i] == 3 && a[i+1] == 1) flag = 1;
          
    }
 //   if(flag)  ans++;   注意这里如果这样写会wa,因为没有判定是否被13整除
    return ans;
}
int main()
{
//freopen("input.txt","r",stdin);
//freopen("output.txt","w",stdout);
    init();
    int i,j;
    /*for(i = 0; i < 10; i++){
          for(j = 0; j < 13; j++)
             cout<<dp[i][j][2]<<" ";
           cout<<endl;
             }
    */
    int n;
    while(scanf("%d",&n) != EOF)
    {
         printf("%d\n",fun(n+1));
         
    }
    
  //  P;                               
    return 0;    
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值