The 2018 ACM-ICPC上海大都會賽 J Beautiful Numbers(數位dp)

題目鏈接:

Beautiful Numbers

 

題意:

給定一個 n ,求 1 - n 中 Beautiful Numbers 的個數。一個數爲 Beautiful Numbers 當且僅當它是它所有數位上數字和的倍數。

 

思路:

數位dp的關鍵在於定義dp數組以確保該數位上進行如此選擇對答案的貢獻是唯一的。

定義dp數組:dp[pos][sum][res] :sum表示各數位上數字的和,res表示數值%mod

其中 mod 爲最終的數字各數位上數字的和。(sum最終要等於mod才計算答案)10^12以內的數mod最大爲位數*9,暴力枚舉即可。

分析唯一性:設到pos位,2個不同的數,及pos位以前的值有不同,但其各數位上數字的和相同,均爲sum,且膜上mod的值(設爲p)也相同,所以從pos到第一位他們所需要的數位和是相同的(均爲mod-sum),且組成的數字%mod的值(設爲 t )也是相同的(均爲 t=mod-p),即這兩個不同的數到此位爲止後面的計算都是完全一樣的,可行!

memset優化:上述dp數組在遍歷mod的時候每次都需要初始化,因爲不同mod情況下,dp數組記錄的數值不同。這樣每次都要初始化15*120*120的數組,單次複雜度爲15*120*120*108 = 2e7,100組詢問,複雜度爲 2e9 ,爆炸。但我們可以發現不同組詢問的時候,只要mod值一樣,其實dp數組存的數值是一樣的,因此爲了避免重複memset,我們可以給dp數組增開一維 [mod] 。即dp數組爲 :dp[pos][sum][res][mod] 。這樣只需要在 T組 外面memset一次即可。(以空間換時間)

 

Code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int MAX = 5e5 + 100;

ll n, mod;
int bit[15];
ll dp[15][120][120][120];

ll dfs(int pos, ll val, int sum, int limit)
{
	if (pos == 0) {
		return (sum == mod) && (val % mod == 0);
	}
	if (!limit&&dp[pos][sum][val % mod][mod] != -1)	return dp[pos][sum][val % mod][mod];
	int up = limit ? bit[pos] : 9;
	ll ans = 0;
	for (int i = 0; i <= up; i++) {
		if (sum + i > mod)	break;
		ans += dfs(pos - 1, val * 10 + i, sum + i, limit&&i == bit[pos]);
	}
	if (!limit&&sum != 0)	dp[pos][sum][val % mod][mod] = ans;
	return ans;
}

ll solve(ll x)
{
	int cnt = 0;
	while (x) {
		bit[++cnt] = x % 10;
		x /= 10;
	}
	ll ans = 0;
	for (int i = 1; i <= cnt * 9; i++) {
		mod = i;
		ans += dfs(cnt, 0, 0, true);
	}
	return ans;
}

int main()
{
	memset(dp, -1, sizeof(dp));
	int T;
	scanf("%d", &T);
	int Case = 1;
	while (T--)
	{
		scanf("%lld", &n);
		printf("Case %d: ", Case++);
		printf("%lld\n", solve(n));
	}
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章