您的位置:首页 > 其它

数位dp(dfs版)

2016-09-22 09:34 225 查看
题目:http://acm.split.hdu.edu.cn/showproblem.php?pid=3555

题意:给定一个上限n,求1--n中存在多少个数,数中含有49

题解:dfs数位dp,此题一共三种状态:st:0:前一个数为4的状态,此位加9就可满足题意;1:前面数字中已经含有49;2:前面中没有49并且前一位不是4

状态转移:

状态49其他数字
0012
1111
2022
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>

using namespace std;
long long dp[25][5];
int num[25];//分解输入上限
long long dfs(int pos,int st,bool f)//表示dp[pos][st]为pos位st状态,f代表pos位是否受限,即表示可以取0-9任意数字还是规定了最大值
{
if(pos<=0) return (!f&&st==1)?1:0;//只有在st为满足题意得状态时才返回1,此题为1状态时 后面原理同此
if(!f && dp[pos][st]!=-1) return dp[pos][st];
int limit=f?num[pos]:9;//若受限就用分解的最大值来规定最大值
long long ans=0;
for(int i=0;i<=limit;i++)
{
bool next_f =f & (limit==i);//是否受限以及是否达到受限最大值
if(i==4)
{
if(st==0) ans+=dfs(pos-1,0,next_f);
if(st==1) ans+=dfs(pos-1,1,next_f);
if(st==2) ans+=dfs(pos-1,0,next_f);
}
else if(i==9)
{
if(st==0) ans+=dfs(pos-1,1,next_f);
if(st==1) ans+=dfs(pos-1,1,next_f);
if(st==2) ans+=dfs(pos-1,2,next_f);
}
else
{
if(st==0) ans+=dfs(pos-1,2,next_f);
if(st==1) ans+=dfs(pos-1,1,next_f);
if(st==2) ans+=dfs(pos-1,2,next_f);
}
}
if(!f) dp[pos][st]=ans;//若不受限就是统计了后面位数的数字和,可以记录
return ans;
}
long long solve(long long d)
{
int pos=1;
while(d)
{
num[pos++]=d%10;d/=10;
}
return dfs(pos-1,2,1);//注意要从2状态开始
}
int main()
{
memset(dp,-1,sizeof(dp));
int t;
scanf("%d",&t);
while(t--)
{
long long right;
scanf("%lld",&right);
printf("%lld\n",solve(right+1));
}
return 0;
}

题目:http://acm.split.hdu.edu.cn/showproblem.php?pid=2089
题意:给定上限和下限,求在这范围内不含62和4的数字个数

题解:dfs数位dp,状态:st:0:满足含有62或4的数字的个数;1:不含62和4中前一位数字是6,+2就变成满足题意;2:不含62和4并且前一位不是6,状态同上一题

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>

using namespace std;
long long dp[25][5];
int num[25];
long long dfs(int pos,int st,bool f)
{
if(pos<=0) return (!f&&st==0)?1:0;//同意从满足题意得状态返回1
if(!f && dp[pos][st]!=-1) return dp[pos][st];
int limit=f?num[pos]:9;
long long ans=0;
for(int i=0;i<=limit;i++)
{
bool next_f =f & (limit==i);
if(i==4)
{
if(st==0) ans+=dfs(pos-1,0,next_f);
if(st==1) ans+=dfs(pos-1,0,next_f);
if(st==2) ans+=dfs(pos-1,0,next_f);
}
else if(i==6)
{
if(st==0) ans+=dfs(pos-1,0,next_f);
if(st==1) ans+=dfs(pos-1,1,next_f);
if(st==2) ans+=dfs(pos-1,1,next_f);
}
else if(i==2)
{
if(st==0) ans+=dfs(pos-1,0,next_f);
if(st==1) ans+=dfs(pos-1,0,next_f);
if(st==2) ans+=dfs(pos-1,2,next_f);
}
else
{
if(st==0) ans+=dfs(pos-1,0,next_f);
if(st==1) ans+=dfs(pos-1,2,next_f);
if(st==2) ans+=dfs(pos-1,2,next_f);
}
}
if(!f) dp[pos][st]=ans;
return ans;
}
long long solve(long long d)
{
int pos=1;
while(d)
{
num[pos++]=d%10;d/=10;
}
return dfs(pos-1,2,1);
}
int main()
{
memset(dp,-1,sizeof(dp));
long long left,right;
while(~scanf("%lld %lld",&left,&right)&&left+right!=0)
{
printf("%lld\n",right-left+1-solve(right+1)+solve(left));
}
return 0;
}

题目:http://acm.split.hdu.edu.cn/showproblem.php?pid=5898

题意:给定上下限,求在范围区间内满足连续奇数位为偶数并且连续偶数位为奇数的数字

题解:dfs数位dp,st:0:奇奇;1:奇偶;2:偶奇;3:偶偶;4:前导0(前一位为0)

状态转移:

状态\加值01、3、5、7、9(奇数)2、4、6、8(偶数)
0x2x
1303
2101
31x1
4401
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>

using namespace std;
long long dp[25][6];
/*
* 奇奇 0
* 奇偶 1
* 偶奇 2
* 偶偶 3
* 前导0 4
*/
int num[25];
long long dfs(int pos,int st,bool f)
{
if(pos<=0) return (!f&&(st==1||st==2))?1:0;//1、2满足题意,所以只有他们返回0,原理同上
if(!f && dp[pos][st]!=-1) return dp[pos][st];
int limit=f?num[pos]:9;
long long ans=0;
for(int i=0;i<=limit;i++)
{
bool next_f =f & (limit==i);
if(i==0)
{
if(st==0) ans+=0;
if(st==1) ans+=dfs(pos-1,3,next_f);
if(st==2) ans+=dfs(pos-1,1,next_f);
if(st==3) ans+=dfs(pos-1,1,next_f);
if(st==4) ans+=dfs(pos-1,4,next_f);
}
else if(i & 1)
{
if(st==0) ans+=dfs(pos-1,2,next_f);
if(st==1) ans+=dfs(pos-1,0,next_f);
if(st==2) ans+=dfs(pos-1,0,next_f);
if(st==3) ans+=0;
if(st==4) ans+=dfs(pos-1,0,next_f);
}
else
{
if(st==0) ans+=0;
if(st==1) ans+=dfs(pos-1,3,next_f);
if(st==2) ans+=dfs(pos-1,1,next_f);
if(st==3) ans+=dfs(pos-1,1,next_f);
if(st==4) ans+=dfs(pos-1,1,next_f);
}
}
if(!f) dp[pos][st]=ans;
return ans;
}
long long solve(long long d)
{
int pos=1;
while(d)
{
num[pos++]=d%10;d/=10;
}
return dfs(pos-1,4,1);
}
int main()
{
memset(dp,-1,sizeof(dp));
int t;
scanf("%d",&t);
long long left,right;
for(int i=1;i<=t;i++)
{

scanf("%lld %lld",&left,&right);
printf("Case #%d: %lld\n",i,solve(right+1)-solve(left));
}
return 0;
}

可见,模板如下:
long long dp[25][6];
int num[25];
long long dfs(int pos,int st,bool f) //pos 当前位数,st当前状态,f是否受限
{
if(pos<=0) return (!f&&(st==xxxx))?1:0;//满足题意的状态返回1,其他返回0
if(!f && dp[pos][st]!=-1) return dp[pos][st];//记忆化,之前计算过的不用再计算
int limit=f?num[pos]:9;
long long ans=0;
for(int i=0;i<=limit;i++)
{
bool next_f =f & (limit==i);//判断下一位是否受限
if(i==.....) //枚举状态转移条件,列出状态转移方程
{
..........
}
}
if(!f) dp[pos][st]=ans;//记忆化
return ans;
}
long long solve(long long d) //位数分解
{
int pos=1;
while(d)
{
num[pos++]=d%10;d/=10;
}
return dfs(pos-1,4,1);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数位dp dfs