您的位置:首页 > 其它

BZOJ 1026 windy数 (数位DP)

2015-03-10 13:45 453 查看
题目链接:BZOJ 1026

我开始是用记忆化搜索写的,然后前导零处理挂了。然后改用数位DP的一般形式写的。写数位DP的一般过程可以总结为先预处理符合条件的没有限制的数,然后将小于限制的直接相加,然后再处理边界上的数(边界处理各种蛋疼)。数位DP的一个重要的思想就是逐位计算。

两篇比较好的国家集训队论文:浅谈数位统计类问题            数位计数问题解法研究

然后是我的代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
using namespace std;

int base[20],f[20][20],dp[20][20];

inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}

void init(){
base[1]=1;//每一位的位权
for(int i=2;i<=10;i++)base[i]=base[i-1]*10;
for(int i=0;i<=9;i++)f[1][i]=1;//预处理每一位没有限制时符合条件的数的个数
for(int i=2;i<=10;i++)
for(int j=0;j<=9;j++)
for(int k=0;k<=9;k++)
if(abs((double)j-k)>=2)f[i][j]+=f[i-1][k];
}

int find(int x){
if(!x)return 0;
int t=0,w=10;
while(base[w]>x)w--;//找到x是几位数
for(int i=1;i<w;i++)//小于该位的没有前导零的方案数直接相加
for(int j=1;j<=9;j++)
t+=f[i][j];
//////////////////////////////////////////////////////////////
//考虑是w位数的情况
int limit=x/base[w];//limit为该位的限制
for(int i=1;i<limit;i++)t+=f[w][i];
x%=base[w];
int pre=limit;
for(int i=w-1;i>=1;i--){
limit=x/base[i];
if(i!=1){
for(int j=0;j<limit;j++)
if(abs((double)pre-j)>=2)t+=f[i][j];
}
else{
for(int j=0;j<=limit;j++)
if(abs((double)pre-j)>=2)t+=f[i][j];
}
if(abs((double)pre-limit)<2)break;//不合限制直接退出循环防止多算
x%=base[i];
pre=limit;
}
return t;
}

int main(){
init();
int A=read(),B=read();
printf("%d\n",find(B)-find(A-1));
return 0;
}

记忆化搜索写的先挖个坑之后再填吧。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: