HDU 3555 Bomb(數位dp)
Description:
The counter-terrorists found a time bomb in the dust. But this time the terrorists improve on the time bomb. The number sequence of the time bomb counts from 1 to N. If the current number sequence includes the sub-sequence “49”, the power of the blast would add one point. Now the counter-terrorist knows the number N. They want to know the final points of the power. Can you help them?
Input:
The first line of input consists of an integer T (1 <= T <= 10000), indicating the number of test cases. For each test case, there will be an integer N (1 <= N <= 2^63-1) as the description. The input terminates by end of file marker.
Output
For each test case, output an integer indicating the final points of the power.
Sample Input:
3 1 50 500
Sample Output:
0 1 15
Hint:
From 1 to 500, the numbers that include the sub-sequence “49” are “49”,“149”,“249”,“349”,“449”,“490”,“491”,“492”,“493”,“494”,“495”,“496”,“497”,“498”,“499”, so the answer is 15.
題目連結
之前寫這種題(HDU 2089、2018年全國多校演算法寒假訓練營練習比賽(第二場)G)是直接對資料範圍內所有數進行判斷打表再在詢問時迴圈計數,但是由於這題資料範圍很大不能這麼寫,所以就學習了一下數位dp。
此題數位dp中dp[i][j]代表i位數中首位為j時不含49的數字個數。
那麼狀態轉移公式就為,其中j=4和k=9不能同時滿足。
那麼對於詢問N,在計算1-N中不含49數字個數的時候從N的最高位Len開始求所有之和並計入結果之中,依次向低位迴圈計數,當且僅當高一位數為4時dp[Len][9]不計數,當N的某兩位為49時跳出迴圈(此時與低位數無關,無論低位數是幾都會含有49)。
最後用總數減去計算得到的不含有49的數量即為含有49的數量。
AC程式碼:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e1 + 5;
long long Dp[maxn][maxn];
void Init() {
memset(Dp, 0, sizeof(Dp));
Dp[0][0] = 1;
for (int i = 1; i <= 19; ++i) {
for (int j = 0; j < 10; ++j) {
for (int k = 0; k < 10; ++k) {
if (!(j == 4 && k == 9)) {
Dp[i][j] += Dp[i - 1][k];
}
}
}
}
}
long long Cal(long long X) {
long long Ans = 0, Len = 0;
long long Digit[maxn];
while (X) {
Digit[++Len] = X % 10;
X /= 10;
}
Digit[Len + 1] = 0;
for (int i = Len; i > 0; --i) {
for (int j = 0; j < Digit[i]; ++j) {
if (Digit[i + 1] != 4 || j != 9) {
Ans += Dp[i][j];
}
}
if (Digit[i + 1] == 4 && Digit[i] == 9) {
break;
}
}
return Ans;
}
int main(int argc, char *argv[]) {
Init();
int T;
scanf("%d", &T);
for (int Case = 1; Case <= T; ++Case) {
long long N;
scanf("%lld", &N);
printf("%lld\n", N + 1 - Cal(N + 1));
}
return 0;
}