我在这个博客上看的 http://www.cnblogs.com/ronaflx/archive/2011/04/18/2020141.html#2309504
然后又问了下 ronaflx 大牛,给了我一种新的方法和代码 且 和我的想法又像就记录下。
按位dp看过很多遍,期间fatboy ZaviOr 也讲过但始终觉得代码不舒服就放一边没认真学。这种格式总算习惯了,简洁又好想。
-----------------------------------------------------------------------------------------------------------------
题目求 1 - N 中含49的个数。 学过ac自动机的可能立即就想到:设三种状态
/*
* 0 不含49且以非4的数字结尾
* 1 不含49且以4这个数字结尾
* 2 含49的状态
*/
这三种状态可以相互转化:
dp[k][0] = dp[k - 1][1] * 8 + dp[k - 1][0] * 9;
dp[k][1] = dp[k - 1][0] + dp[k - 1][1];
dp[k][2] = dp[k - 1][2] * 10 + dp[k - 1][1];
再把整数N看成字符串,就可以从左到右dp过去。 这就是按位dp了。
但没那么简单,例如 N = 12345
考察第一位:如果取 0 ,第二位可以取遍0-9所有数(即后面的可以按自动机那个递推关系直接算出来); 如果取 1 ,第二位最大取2了。
所以,某一位在DP的过程中往右转移,必须分两种情况:到该位的前缀小于上界(如第三位时为122 或109等等),可以直接用自动机方法转移;前缀刚好等于上界(如到第三位时为123),右边一位就不能取遍 0 - 9 的所有数,要DP一下了。
所以,设个三维数组: dp[ 1 - len ] [ 0 1 2 ] [ 0 1 ] 第一维表示字符串从左到右的位置, 第二维表示自动机的三种状态, 第三维表示当前位上的数字是否等于上界(即 从左到该位的前缀都相等。如到第三位时为123**)
然后又问了下 ronaflx 大牛,给了我一种新的方法和代码 且 和我的想法又像就记录下。
按位dp看过很多遍,期间fatboy ZaviOr 也讲过但始终觉得代码不舒服就放一边没认真学。这种格式总算习惯了,简洁又好想。
-----------------------------------------------------------------------------------------------------------------
题目求 1 - N 中含49的个数。 学过ac自动机的可能立即就想到:设三种状态
/*
* 0 不含49且以非4的数字结尾
* 1 不含49且以4这个数字结尾
* 2 含49的状态
*/
这三种状态可以相互转化:
dp[k][0] = dp[k - 1][1] * 8 + dp[k - 1][0] * 9;
dp[k][1] = dp[k - 1][0] + dp[k - 1][1];
dp[k][2] = dp[k - 1][2] * 10 + dp[k - 1][1];
再把整数N看成字符串,就可以从左到右dp过去。 这就是按位dp了。
但没那么简单,例如 N = 12345
考察第一位:如果取 0 ,第二位可以取遍0-9所有数(即后面的可以按自动机那个递推关系直接算出来); 如果取 1 ,第二位最大取2了。
所以,某一位在DP的过程中往右转移,必须分两种情况:到该位的前缀小于上界(如第三位时为122 或109等等),可以直接用自动机方法转移;前缀刚好等于上界(如到第三位时为123),右边一位就不能取遍 0 - 9 的所有数,要DP一下了。
所以,设个三维数组: dp[ 1 - len ] [ 0 1 2 ] [ 0 1 ] 第一维表示字符串从左到右的位置, 第二维表示自动机的三种状态, 第三维表示当前位上的数字是否等于上界(即 从左到该位的前缀都相等。如到第三位时为123**)
************************************************************************************************************************************
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<memory.h>
#include<time.h>
using namespace std;
typedef __int64 LL;
const int N = 22;
char s[N];
LL dp[N][3][2];
/*
* 0 不含49,末尾不是4
* 1 不含49,末尾是4
* 2 含49
* 0 没到上界; 1 到上界
*/
int next(int now,int bit){ //状态转移,当前是 now状态,后面加一位数 bit 得到的新状态
if(now==2 || now==1&&bit==9) return 2;
return bit == 4;
}
LL DP(char *s,int len){
memset(dp,0,sizeof(dp));
dp[0][0][1] = 1;
for(int i=1;i<=len;++i){ //字符串
//from 0 to 0
dp[i][0][0] = dp[i-1][0][0]*9 + dp[i-1][1][0]*8;
dp[i][1][0] = dp[i-1][0][0] + dp[i-1][1][0];
dp[i][2][0] = dp[i-1][2][0] * 10 + dp[i-1][1][0];
//from 1 to 0
for(int j=0;j<s[i]-'0';++j){
dp[i][next(0,j)][0] += dp[i-1][0][1];
dp[i][next(1,j)][0] += dp[i-1][1][1];
dp[i][next(2,j)][0] += dp[i-1][2][1];
}
//from 1 to 1
dp[i][next(0,s[i]-'0')][1] += dp[i-1][0][1];
dp[i][next(1,s[i]-'0')][1] += dp[i-1][1][1];
dp[i][next(2,s[i]-'0')][1] += dp[i-1][2][1];
}
return dp[len][2][0] + dp[len][2][1];
}
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%s",s+1);
printf("%I64d\n",DP(s,strlen(s+1)));
}
return 0;
}