您的位置:首页 > 其它

hdu 3652 -数位dp

2013-09-10 23:36 260 查看
之前学校新生培训的时候学长挂的题,当时给了标准代码没看明白,放了半年,今天花一天总算看明白了,从这个过程也能看出这半年学习是有不少进步的,代码的理解有一定的加深,不过单看数位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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数位dp