动态规划(DP)不要62

来源:互联网 发布:院长入额首选知产团队 编辑:程序博客网 时间:2024/04/29 01:39

不要62

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 14020    Accepted Submission(s): 4501


Problem Description
杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer)。
杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍,更安全地服务大众。
不吉利的数字为所有含有4或62的号码。例如:
62315 73418 88914
都属于不吉利号码。但是,61152虽然含有6和2,但不是62连号,所以不属于不吉利数字之列。
你的任务是,对于每次给出的一个牌照区间号,推断出交管局今次又要实际上给多少辆新的士车上牌照了。
 

Input
输入的都是整数对n、m(0<n≤m<1000000),如果遇到都是0的整数对,则输入结束。
 

Output
对于每个整数对,输出一个不含有不吉利数字的统计个数,该数值占一行位置。
 

Sample Input
1 1000 0
 

Sample Output
80
 

Author
qianneng
 

Source
迎接新学期——超级Easy版热身赛
 

Recommend
lcy
 

题意:问在n,m之间不含4和62序列(4和62为不吉利数)的数字有多少个。
题解:把状态分为3种,

dp[i][0],表示不存在4和62,最高位是不是2没有要求.
dp[i][1],表示不存在4和62,且最高位为2(dp[i][1]是dp[i][0]的一个子集)

dp[i][2],表示存在4或62


预处理的过程十分类似进制转换的求程,例如,求(a3a2a1a0)10转化为二进制的过程,我们会预处理:

bit0=2^0

bit1=2^1

bit2=2^2

bit3=2^3

然后result = a3*bit3+a2*bit2+a1*bit1+a0*bit0.


状态转移方程


dp[i][0]=dp[i-1][0]*9-dp[i-1][1];                            //在高位补除4外的9个数,因为补了6,需要将下一位为2的情况去掉
dp[i][1]=dp[i-1][0];                                               //在高位补2
dp[i][2]=dp[i-1][2]*10+dp[i-1][1]+dp[i-1][0];         //三种情况,低位已经有倒霉数,则高位任取10种可能值,低位为2,

    高位取6,低位没有倒霉数,高位取4

如果将dp[i][j]看作进制转换的基底,将s[i]看作当前位的数值,则下面的代码看起来十分类似进制转换.具体从代码中体会.

#include<cstdio>#include<cstring>#include<iostream>using namespace std;int dp[10][3];int DP(int x){bool isUnlucky=false; //初始设为false,初始状态是幸运的int s[15];int Index=0,summ=x,unLuckyNum=0;//为了适合人类习惯,反转数字,且从1开始数起for(;x;x/=10)s[++Index] = x % 10 ;s[Index+1]=0;for(int i=Index;i>0;--i) //处理顺序从高到低{//如果本位是s[i],而且前一种状态存在倒霉数,则倒霉数会新增s[i]*dp[i][2] (1)//例如s[i][****4****],s[i][***62*****],s[i][***4****62***]这种情况,对应dp[i][2],unLuckyNum+=(dp[i-1][2]*s[i]);//分两个情况处理if(isUnlucky) //case1:出现了倒霉数,4,62,这是上一轮产生的倒霉数,例如62[****],//(i指向2的下一位),或者4[******],(i指向4的下一位),与(1)区分开来//如果isUnlucky,实际上执行了两步,(1)处理历史遗留问题s[i][****4****],s[i][***62*****],//s[i][***4****62***],这里的[]属于dp[i][2],//(2)处理新产生的问题,例如:62[***]或4[****],这里的[]属于dp[i][0](低位可取任意值)//本位已经是倒霉数,低位是任意数它都会是倒霉数,所以,统计后面的所有情况即可//(不需再处理dp[i][2],上面已经处理过了),难点!类比进制转换就明白了unLuckyNum+=(dp[i-1][0]*s[i]);else         {//case2:不存在4或62(即只能在dp[i][0]和dp[i][1]中取)//>4的意思是必定途经4(是否途经6下一步再判断),例如5,则累计的时候需要//统计1,2,3,4,5.5种情况最高位取4,则后面任取,实际上,一个数"仅仅"大于4(类似5),//只执行了一步操作:unLuckyNum+=dp[i-1][0];if(s[i]>4)            unLuckyNum+=dp[i-1][0];//出现4和6,统计前一状态最高位是2的情况,则倒霉数出现的情况是本位为6,前一状态最高位是2//实际上,一个数如果>6,则它也>4,所以>6执行了这两步操作:unLuckyNum+=dp[i-1][0],(高位是4时)//unLuckyNum+=dp[i-1][1];(高位是6位时)if(s[i]>6)unLuckyNum+=dp[i-1][1]; //处理本位是6的情况,本位是4的情况上面已经处理过了if(s[i+1]==6&&s[i]>2)unLuckyNum+=dp[i][1];//如果高位出现6,则取本位出现2的情况}if(s[i]==4||s[i+1]==6&&s[i]==2) //判断本位是否unluckyisUnlucky=true;}return summ-unLuckyNum;//所有的数减去倒霉数}int main(){int a,b;memset(dp,0,sizeof(dp));//预处理,算出所有可能dp[0][0]=1; //dp[i][0],表示不存在4和62,最高位是不是2没有要求.//dp[i][1],表示不存在4和62,且最高位为2(dp[i][1]是dp[i][0]的一个子集//dp[i][2],表示存在4或62for(int i=1;i<=8;++i){//高位分别补除了4的9个数字,因为补了6,所以需要减去前一种最高位是2的情况dp[i][0]=dp[i-1][0]*9-dp[i-1][1];//在不含倒霉数的高位补2dp[i][1]=dp[i-1][0];//各种出现4和62的情况,前一种情况已经出现了倒霉数,则高位有10种取法,//前边最高位是2,则高位取6,//前边没有倒霉数且最高位不是2,则高位取4dp[i][2]=dp[i-1][2]*10+dp[i-1][1]+dp[i-1][0];}/*for(int i=0;i<10;++i){for(int j=0;j<3;++j)cout<<dp[i][j]<<' ';cout<<endl;}*/for(;scanf("%d%d",&a,&b);){if(a+b==0) break;printf("%d\n",DP(b+1)-DP(a));}return 0;}

上面的方法思维复杂,用时少,下面还有一种方法容易理解,但是,用时稍多,125ms。只需要蛮力打表。
#include <iostream>#include <string>using namespace std;bool luck[1000001];void SetLucky(){    int i, tmp;    for (i=1; i <= 1000000; ++i)    {        tmp = i;        while (tmp != 0)        {            if (tmp%10==4 || tmp%100==62)    //i不吉利                break;            tmp /= 10;        }        if (tmp)            luck[i] = false;        else            luck[i] = true;    }}int main(){#ifndef ONLINE_JUDGE    freopen("2.txt","r",stdin);#endif    int b,e,i,sum;    SetLucky();    while (~scanf("%d%d",&b,&e) && !(b==0&&e==0))    {        sum = 0;        for (i=b; i <= e; ++i)        {            if (luck[i])                ++sum;        }        printf("%d\n",sum);    }    return 0;}



原创粉丝点击