HDU2089(数位dp入门)

来源:互联网 发布:win10平板安装ubuntu 编辑:程序博客网 时间:2024/06/06 09:56
#include<iostream>#include<string>#include<queue>#include<stdio.h>#include<string.h>#include<stdlib.h>#include<math.h>using namespace std;#define Max(a,b)(a>b?a:b)#define Mem(a,b) (memset(a,b,sizeof(a)))#define Abs(a)(a>0?a:-a)#define INF 1e20#define EPS 1e-8#define ll long longint dp[10][3];//dp[i][j],i代表数字的位数,j代表状况//dp[i][0],表示都是吉利数字//dp[i][1],表示都是吉利数字,且最高位为2   2xxxxxx//dp[i][2],表示所有不吉利数字   4xxxx ||  xx4xx || 62xxxx || xx62xxvoid Init(){int i;Mem( dp, 0 );dp[0][0] = 1;for( i= 1; i<= 6; i++ ){dp[i][0] = dp[i-1][0] * 9 -dp[i-1][1];//当前最高位加上不含4的9个数字的情况  //如当前位为1 则 1+1xx 1+2xx 1+3xx 1+5xx 1+6xx 1+7xx 1+8xx 1+9xx 1+0xx 所以在上一位上*9即可  //但是存在一种情况:当前最高位为6,那么要减去上一次最高位为2的情况                                      //因为这次最高位6和上次最高位2构成了不吉利的数字  //我们用dp[i-1][1] 记录了上一次最高位为2的情况的所有的数字,因此减去即可dp[i][1] = dp[i-1][0];                //用来记录当前最高位为2的情况。就是记录2+xx的数字的个数dp[i][2] = dp[i-1][2] * 10 + dp[i-1][0] + dp[i-1][1]; //已经含有的前面放什么都可以。  //dp[i-1][2]*10 表示前面所有的不吉利的数字在增加一个最高位之后,不吉利的数字也会增加10倍  //比如 如果一个最高位则就只有一个不吉利的数字,就是4,当增加一个最高位之后,就增加了10倍,40,41,42,43,44,45,46,47,48,49  //加上dp[i-1][0]的情况是 在上一次的所有的吉利的数字前面加上一个4.则上一次的吉利的数字都成了不吉利的数字  //加上dp[i-1][1]的情况是 在上一次的所有的最高位为2的吉利数字前加上一个6,则上一次的吉利数字都成了不吉利的数字}//如此循环6遍之后,最高位就有了6位,即从1~1000000//dp[i][1]只是用来记录最高位为2的数字。//dp[i][0]+dp[i][2] 的和 等于 所有的数字。即dp[i][0]代表所有吉利的数字,dp[i][2]代表所有不吉利的数字。因为上面一句已经讲到dp[i][1]只是用来记录最高位为2的数字。}int solve(int n){int k;    int i, len = 0, tem = n, ans, flag, a[10];    while( n )//将每一位拆分放入数组    {        a[ ++len ] = n % 10;        n /= 10;    }    a[ len+1 ] = ans = 0;//ans代表不吉利的数字的个数    flag = 0;    for( i = len; i >= 1; i-- )    {        ans += dp[ i-1 ][ 2 ] * a[ i ];//上一次所有不吉利的数字*这次最高位的数字,表示前面所有的不吉利的数字在增加一个最高位之后,不吉利的数字也会增加这次最高位的数字大小倍        if( flag )//如果已经是不吉利了,则该位之后的数字都不是吉利数字,所以要加上这些吉利的数字(之后都变成了不吉利的)。因为在函数Init中讲到 dp[i][0]+dp[i][2] 的和 等于 所有的数字。而ans代表的是不吉利的数字的个数。            ans += dp[ i-1 ][ 0 ] * a[ i ];        if( ! flag && a[ i ] > 4)//如果此时最高位大于4,则存在有4+xxxxx的情况,则要加上4+xx的情况的数字的个数            ans += dp[ i - 1 ][ 0 ];        if( ! flag && a[ i + 1 ] == 6 && a[ i ] > 2 )//如果此时最高位大于2,且下一个最高位为6,则存在62+xxx的情况,则要加上62+xx的情况的数字的个数            ans += dp[ i ][ 1 ];        if( ! flag && a[ i ] > 6)//如果此时最高位大于6,则存在6+xx的情况,考虑到上一个最高位可能为2,所有存在62+xx的情况,则要加上62+xx的情况的数字的个数            ans += dp[ i - 1 ][ 1 ];        if( a[ i ] == 4 || ( a[ i + 1 ] == 6 && a[ i ] == 2 ) )//标记为不吉利            flag = 1;    }    return tem - ans;}int main(){    int l, r;    Init();    while( ~scanf( "%d%d", &l , &r ) , l + r )    {        printf( "%d\n", solve( r + 1 ) - solve( l ) );        //因为solve函数中并没有考虑n是不是不幸数的情况,所以r+1只算了1~r,而l只算了1~l-1,这两者相减才是正确答案    }    return 0;}

0 0