HDU-#2089不要62(数位DP)

来源:互联网 发布:文明 太空 mac 中文 编辑:程序博客网 时间:2024/04/30 09:47

    

     方法一:由于数据量不是很大,可以直接打表。解题思路:先将1到1000000的所有数据进行遍历,找到含4或者62的数据,对该位置进行标记。再对输入的n和m的区间进行判断,看是否标记,标记则计数。因为有的OJ计算时间是从输入数据开始到输出进行计时的,因此直接暴力求解会超时,但打表不会。

     AC code:

#include <iostream>#include <cstring>using namespace std;#define MAXN 1000010bool table[MAXN];int n,m,count;int main(){    int sum,temp;    memset(table,0,sizeof(table));    for(int i=1;i<1000001;i++){//打表,在数据输入之前完成        sum=temp=i;        while(temp>0){            if(temp % 10 == 4 || temp % 100 == 62)                table[sum]=1;                temp=temp/10;        }    }    while(cin>>n>>m && (n||m)){//判断表中满足条件的数量        count = m-n+1;        for(int j=n;j<=m;j++){            if(table[j]==1)                count--;        }        cout<<count<<endl;    }    return 0;}

    方法二:该题是属于数位DP,解题思路见注释,还是要去看些理论吧,有点不好理解的样子。

   AC code:

#include<cstdio>#include<cstring>using namespace std;int digits[15];//用来存放每一位的数字int dp[10][3];/*dp[i][0]不存在不吉利数的个数;dp[i][1]不存在不吉利数且首位为2的数的个数;dp[i][2]存在不吉利数的个数*/int DP(int x){    bool flag=false;    int temp=0,sum=x,ans=0;    for(;x;x/=10)//循环去掉低位,便于下次循环每位存取        digits[++temp]=(x%10);//存放每一位的值    digits[temp+1]=0;    for(int i=temp;i>0;--i){        ans+=(dp[i-1][2]*digits[i]);//由上位所有不吉利数推导        if(flag) ans+=(dp[i-1][0]*digits[i]);//如果前面出现过4,或者62,则不含不吉利数字的数量得乘本位        else        {            if(digits[i]>4) ans+=dp[i-1][0];//如果本位大于4,不含不吉利数字的数量得乘本位,因为前面的数位是确定的            if(digits[i]>6) ans+=dp[i-1][1];//如果本位大于6,前一位带2不含不吉利数字的数量乘本位            if(digits[i+1]==6&&digits[i]>2) ans+=dp[i][1];//如果本位大于2,而且前一位等于6,i长度以2开头的含不吉利数字的数量乘于本位        }        if(digits[i]==4||digits[i+1]==6&&digits[i]==2)//为不吉利数字则标记            flag=true;    }    return sum-ans;//所有的数减去不吉利的数}/*预处理,算出所有可能,下面计算时,直接调用*/void Init(){    dp[0][0]=1;    for(int i=1;i<8;++i){        dp[i][0]=9*dp[i-1][0]-dp[i-1][1];//在不含不吉利数的末位分别补除了4的9个数字,减去在6后面补2的个数        dp[i][1]=dp[i-1][0];//最高位为2        dp[i][2]=10*dp[i-1][2]+dp[i-1][0]+dp[i-1][1];//在已经有不吉利数字最高位加任意数字或在无吉利数字前加4或在2前加4    }}int main(){    int n,m;    memset(dp,0,sizeof(dp));    Init();//预处理,算出所有可能    while(scanf("%d%d",&n,&m) && (n || m)){        printf("%d\n",DP(m+1)-DP(n));    }    return 0;}


    方法三:看到一种DFS写的,感觉很不一样,写了很久才调试对,汗。。。。其解题思路为见code。

    code:

#include <cstdio>#include <cstring>using namespace std;int digit[8];//用来存放每一位的数字int dp[8][2];/*dp[i][0]不存在不吉利数的个数;dp[i][1]不存在不吉利数且首位为2的数的个数;dp[i][2]存在不吉利数的个数*/int dfs(int len,bool state,bool ismax){//state记录上一位是否为6,若ismax为true则后面循环的时候i只能取0~digit[len]    if(len==0)  return 1; //能递归到这里说明这串数符合要求,返回1    if(!ismax && dp[len][state]!=-1)  return dp[len][state];//若ismax为true,则还需要继续向下递归    int ret=0,maxnum = ismax ? digit[len]:9;    for(int i=0;i<=maxnum;i++){        if(i==4 || state && i==2) continue;//不能有4,或者前一位为6且该位为2        ret += dfs(len-1,i==6, ismax && i==maxnum);//ismax && i == maxnum 用来判断是否达到位值上限    }    if(!ismax)   dp[len][state]=ret;//根据ismax来决定是否记录dp    return ret;}int f(int n){    int len =0;//记录长度,初始化    while(n){        digit[++len]=n%10;//存放每一位的值        n/=10;//循环去掉低位,便于下次循环每位存取    }    return dfs(len,false,true);//从首位开始递归统计}int main(){    int n,m;    memset(dp,-1,sizeof(dp));//初始化每一位的状态信息    while(scanf("%d%d",&n,&m) && (n||m)){        printf("%d\n",f(m)-f(n-1));    }    return 0;}


0 0
原创粉丝点击