鏈接
來源:牛客網杭州人稱傻乎乎的人爲62,而嘟嘟家這裏沒有這樣的習俗。
相比62,他那裏的人更加討厭數字38,當然啦,還有4這個
數字!所以啊,嘟嘟不點都不想見到包含38或者4的數字。
每次給出一個區間[n,m],你能找到所有令人討厭的數字嗎?
輸入描述:
多組輸入輸出;
輸入的都是整數對n、m(0
#include <iostream>
#include <cstring>
using namespace std ;
const int len = 7 ;
int dp[8][10] , bit[8] ;//全局變量初始爲0,dp[x][y]數組表示長爲x的以y打頭的所有數中“合法”數的個數,
// 注意首位爲0的數也看作x位,例如0123看作4位數,
// 原因:如果不考慮以0開頭的數那麼dp[3][3] = dp[2][9] + dp[2][8] + ...+ dp[2][2] + dp [2][1] + dp[1][9] + dp[1][8] + ... + dp[1][2] + dp[1][1]
// 若把所有一位數看作以0開頭的2位數則實際上dp[3][3] = dp[2][9] + dp[2][8] + ...+ dp[2][2] + dp [2][1] + dp[2][0]
// 所以認爲dp[x][0] = dp[x-1][0...9]
// 我們只需要把dp[2][0...9]這些加起來就好了
int n , m ;
void Get_dp() {
dp[0][0] = 1 ;
for ( int i = 1 ; i <= len ; ++i )//長度i從1到7
for ( int j = 0 ; j <= 9 ; ++j )//數值j從0到9
if ( j != 4 )//排除j等於4的情況
for ( int k = 0 ; k <= 9 ; ++k )//第二個數值k從0到9
if ( !( j == 3 && k == 8 ) )//排除j,k爲3,8的情況
dp[i][j] += dp[i - 1][k] ;//i從1開始所以i-1不會越界且由於是全局變量默認值爲0,所以免去了對i-1是否越界的討論
/**
* 從長爲1位的數,開頭的數從0到9,填表
* 狀態轉移方程:
* dp[x][y] = 0; 若y==4
* dp[x-1][0...9]; 若y!=3(相當於在所有x-1位的合法數前面加了個不是3的數,肯定也是合法數)
* dp[x-1][0...9] - dp[x-1][8]; 若y==3則要排除掉第二位爲8的情況
*/
}
int solve( int n ) {//根據表來得出從1到n(實際上沒有包括n,因爲下面有一句for ( int j = 0 ; j < bit[i] ; ++j )用的小於號,我們先不考慮這細節)的所有合法數的個數
memset( bit , 0 , sizeof( bit ) ) ;//將n分解放入bit數組中
int top = 0 ;//(從n的底位到高位倒序放入)
while ( n ) {
bit[++top] = n % 10 ;
n /= 10 ;
}
int ans = 0 ;//保存所有合法數個數
for ( int i = len ; i >= 1 ; --i ) {//i從數的最高位開始向下
for ( int j = 0 ; j < bit[i] ; ++j )
if ( !( bit[i + 1] == 3 && j == 8 ) )
ans += dp[i][j] ;//排除上一位是3且這一位以8開頭的情況
if ( bit[i] == 4 || ( bit[i + 1] == 3 && bit[i] == 8 ) )
break ;//若這一位是4則跳出循環
}
/**
* 這裏舉個例子解釋一下:
* 假設輸入n爲243
* 那麼我們應該將1...243分解成 000...199 200...239 240...242 243
* 對應一下其實就是 dp[3][0...1] dp[2][0...3] dp[1][0...2] dp[0][]恆爲0(所以代碼中不管數字n是否合法都沒有考慮到,最終返回值其實是1...n-1的所有合法數個數)
* 註解 (1) (2) (3)不合法,之後的也是 (4)不合法
*
* (1)從最高位開始
* (2)00...39中的所有合法數前面加一個2還是合法數
* (3)0...2的前面加上一個4出問題了!!,所有的0...2中的合法數變得不合法,應該排除,接下來所以位的分解前面也會加上4,所以直接跳出循環
* if ( bit[i] == 4 || ( bit[i + 1] == 3 && bit[i] == 8 ) )
* break ;//若這一位是4則跳出循環
* (4)這裏爲什麼單獨分開來?提示:“小於”號“<”
*
* 其實這些分法都是受到了上面的:
* for ( int j = 0 ; j < bit[i] ; ++j )
* 的影響(注意小於號),也就是按照這條語句來分的
* 需要仔細想想
*/
return ans ;
}
int main() {
Get_dp() ;//打表
while ( cin >> n >> m && ( n || m ) )
cout << m - n + 1 - ( solve( m + 1 ) - solve( n ) ) << endl ;
return 0 ;
}