acdream 数树专题--完美数(数位dp)
来源:互联网 发布:网络歌手想你的感觉 编辑:程序博客网 时间:2024/05/20 05:25
/*
题目:完美数
题目链接:http://www.acdream.net/problem.php?id=1083
题目描述:在[L , R] 的正整数区间内,要么包含3 要么包含 8 的不同的整数有多少个?
解题思路:数位dp;
这题主要求出[0,n]区间内有多少个这样的数;
则[L,r]=[0,r]-[0,l-1];
先求出每位数以内有多少这样的数,比如:
dp[i][1]表示在i位数中只包含3不包含8的数的个数
dp[i][2]表示在i位数中只包含8不包含3的数的个数,
dp[i][3]表示在i位数中既不包含8也不包含3的数的个数。
注:i位数就是[0,10^i)以内的数;
这有状态方程转移为:
dp[i][1]=dp[i-1][3]+dp[i-1][1]*9; //表示这一位取3,前面所有的位既不含8也不含3,加上这一位不取8(剩下9个数),上一位数中只取3的数
dp[i][2]=dp[i-1][3]+dp[i-1][2]*9; //表示这一位取8,前面所有的位既不含8也不含3,加上这一位不取3(剩下9个数),上一位数中只取3的数
dp[i][3]=dp[i-1][3]*8; //表示这一位既不取8也不取3,上一位也既不取3,8的数,那么这一位能取的个数就是8;
我们求出某位数之内有多少个这样的数之后,接下来就是逐位分析了;
比如我们要求n以内的所有数,假设n=1386;我们先取n的最高位1,因为最高位是1,那么从0到999以内的数我们可以直接加上(dp[3][1]+dp[3][2]),
接下来的位是3,那么0-299之内的数我们可以加上3*(dp[2][1]+dp[2][2]),因为是3,我们标记一下3已经出现了,接下来一位是8,我们可以加上0-79
之内的数8*dp[1][1](因为已经出现了3,所以dp[1][2]就不能要了),这里还要注意一下,因为前面出现了3,那么我们还要加上8*dp[1][3]。又因为现在
出现了8,再往下就没意义了(8和3不能同时出现),最后答案就是上面的所有数的和。
如果不是因为3和8矛盾跳出循环的,还要考虑最后一位的数字;
注意:逐位分析的时候一定要充分考虑位的相关性,以下是我的代码;
*/
#include<stdio.h>#include<string.h>int dp[15][4];void init(){int i;memset(dp,0,sizeof(dp));dp[0][3]=1;dp[1][1]=1;dp[1][2]=1;dp[1][3]=8;for(i=2;i<11;i++){dp[i][1]=dp[i-1][3]+dp[i-1][1]*9;dp[i][2]=dp[i-1][3]+dp[i-1][2]*9;dp[i][3]=dp[i-1][3]*8;}}int find(int n){int digit[10],i,cnt,tag1=0,tag2=0;if(n<3)return 0;int sum=0,len=0;while(n){digit[++len]=n%10;n/=10;}for(i=len;i>0;i--){if(tag1&&tag2)break;cnt=0;if(tag1||tag2)sum+=digit[i]*(dp[i-1][3]);if(digit[i]>3){cnt++;if(!tag1) sum+=dp[i-1][2]+dp[i-1][3];if(tag1||tag2) sum-=dp[i-1][3];}if(digit[i]>8){cnt++;if(!tag2) sum+=dp[i-1][3]+dp[i-1][1];if(tag1||tag2) sum-=dp[i-1][3];}if(!tag2)sum+=(digit[i]-cnt)*(dp[i-1][1]);if(!tag1)sum+=(digit[i]-cnt)*(dp[i-1][2]);if(digit[i]==3)tag2=1;else if(digit[i]==8)tag1=1;}if(tag1==1-tag2)sum++;return sum;}int main(){int cas,i,l=0,r,sum=0;init();scanf("%d",&cas);while(cas--){scanf("%d%d",&l,&r);if(l>r){i=l;l=r;r=i;}r=find(r);l=find(l-1);printf("%d\n",r-l);}return 0;}
/*
记忆化搜索版-(原理不变,代码更简洁)
(记忆化搜索版)数位dp资料详见博客:http://www.cppblog.com/Yuan/archive/2011/07/15/139299.html
*/
#include<stdio.h>#include<string.h>int dp[11][3],digit[11];int dfs(int pos,int pre,int doing){int i,now,ans=0;if(pos==0) return pre>0;if(!doing&&dp[pos][pre]!=-1)return dp[pos][pre];now=doing?digit[pos]:9;for(i=0;i<=now;i++){if(i==3) ans+=(pre!=2)?dfs(pos-1,1,doing&&i==now):0;else if(i==8) ans+=(pre!=1)?dfs(pos-1,2,doing&&i==now):0;else ans+=dfs(pos-1,pre,doing&&i==now);}if(!doing) dp[pos][pre]=ans;return ans;}int cal(int n){if(n<3)return 0;int len=0;while(n){ digit[++len]=n%10;n/=10;}return dfs(len,0,1);}int main(){memset(dp,-1,sizeof(dp));int cas,l,r;scanf("%d",&cas);while(cas--){scanf("%d%d",&l,&r);printf("%d\n",cal(r)-cal(l-1));}return 0;}
- acdream 数树专题--完美数(数位dp)
- ACdream 1064 完美数(数位dp)
- acdream 1064 完美数 数位dp
- acdream 1064 完美数 (数位dp)
- ACdream 1064 完美数【数位DP】
- ACDream 1064——完美数(数位DP)
- ACdream群OJ-完美数-简单数位dp
- AcDream 1083: 完美数
- ACdream OJ 完美数
- ACDream 1064 完美数
- ACdream 1064 完美数
- ACdream 1064 完美数
- ACdream 完美数
- ACdream 1064完美数
- 51nod1232 完美数 数位DP
- XMUT ACdream DP专场E题 - 喵哈哈的日常选数问题(数位DP)
- windy数(数位DP)
- windy数(数位dp)
- 分布式存储hypertable结构概述
- 九度 1003 A+B 数字以逗号分开
- PhysX 3.2里的布料模拟(1):基本概念
- C++类中成员变量的初始化总结
- 九度 1004 Median
- acdream 数树专题--完美数(数位dp)
- UVA 296 - Safebreaker
- 实验一 简单Java程序设计
- setjmp和longjmp函数使用详解
- [leetcode] word ladder
- extjs4 form.Panel
- 百度面试题:自己实现strlen,考虑32位,64位机器,考虑性能
- BufferedImage互换byte
- C与PHP速度对比