1. 程式人生 > 其它 >HDU - 4389 X mod f(x)

HDU - 4389 X mod f(x)

題目連結:

https://acm.hdu.edu.cn/showproblem.php?pid=4389

題目大意:

給出一個函式 \(f\)

int f ( int x ) {
   if ( x == 0 ) return 0;
   return f ( x / 10 ) + x % 10;
}

\(T\) 組測試,每組給兩個數 \(a\)\(b\),求出在 \([a, b]\) 範圍中的數 \(x\),滿足 \(x % mod f(x) == 0\) 的數量。

思路:

\(f(x)\) 函式的返回值就是 \(x\) 所有位置上的數字和,顯然,通過暴力列舉區間內每個數去計算滿足條件的數字數量的時間複雜度太高。嘗試用其它的方法來求解。
所有位數之和最大隻有 81,所以考慮以所有位數之和來列舉。
定義一個四維的 \(dp\)

陣列 \(dp[pos][sum][mod][res]\),表示列舉到第 \(pos\) 位,所有位數之和為 \(sum\),對 \(mod\) 取模,結果為 \(res\) 的數有多少個。通過記憶化搜尋的方式求值。
在搜尋的過程中,要考慮每一位上的值有沒有限制,如果前面都相等,例如求的數是 1234,列舉到 122X,那麼 X 可以從 0 取到 9,如果是 123X,那 X 最大隻能到 4 了。所以 \(dfs\) 的過程中還要再多一個引數,記錄當前求的這一個狀態有沒有限制。
剛開始列舉的是最高位,從最高位依次列舉到最低位,結束的狀態就是當列舉完每一位,若位數之和就是取模的數,且餘數為 0,說明取模之後結果為 0,那答案就 +1,否則不加。
當某個狀態已經被記錄了,那麼直接返回該值,不再重複計算,降低時間複雜度。
從某個狀態轉移到下一個狀態,\(sum\)
要加上這個數,取模不變,餘數從 \(res\) 變成了 \((res * 10 + i) % mod\),即加入新的數之後取模留下的餘數。

程式碼:

#include <bits/stdc++.h>
using namespace std;
const int N = 11, M = 82;
int T, a, b, d[N], dp[N][M][M][M];
int dfs(int pos, int sum, int mod, int res, bool f){
	if (pos == 0){
		if (sum == mod && res == 0) return 1;
		else return 0;
	}
	if (!f && dp[pos][sum][mod][res] != -1) return dp[pos][sum][mod][res];
	int ans = 0, c = !f ? 9 : d[pos];
	for (int i = 0; i <= c; i ++ )
		ans += dfs(pos - 1, sum + i, mod, (res * 10 + i) % mod, f && i == d[pos]);
	if (!f) dp[pos][sum][mod][res] = ans;
	return ans;
}
int solve(int x){
	int len = 0;
	while (x){
		d[++len] = x % 10;
		x /= 10;
	}
	int ans = 0;
	for (int i = 1; i <= 81; i ++ )
		ans += dfs(len, 0, i, 0, true);
	return ans;
}
int main(){
	memset(dp, -1, sizeof dp);
	cin >> T;
	for (int i = 1; i <= T; i ++ ){
		cin >> a >> b;
		printf("Case %d: %d\n", i, solve(b) - solve(a - 1));
	}
	return 0;
}