做了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;
}