題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=3709
記憶化 ,用的數位dp的模式感覺 ,數位dp也是菜開始學的 感覺很難看了一些博客 最終艱難的把它a了,寫個題解來鞏固一下把儘量寫得詳細點。
網上很多博客都說直接枚舉支點 其實對這個不是很熟,下面就用我的話來表述一下這個題吧。
首先是暴力dfs,對於指定長度的一個數,小於它的數有多少是平橫數,根據自己理解到的數位dp的東西,根據指定長度,那麼對於長度達不到的數前面補0,這樣就避免了位數的關係 而且這樣也可以保證每一位數都能枚舉到,那麼這個題 我們枚舉那個數位是平衡點 假設是第i位是平衡點 那麼只需要重左加到右每次加的時候乘上位置差就可以了。
然後對於dp記憶 dp[i][j][sum] 表示剩餘長度爲i的時候 平衡點爲就的時候 兩邊邊差爲sum的時候有多少種平衡情況 注意havemax標記 因爲如果前一位不是最大值得時候那麼當前這一爲可以隨便取0~9 否則就只能取到當前位的最大值。注意dp記錄的也只能是沒有最大值得情況 因爲能對於每一種情況 如果沒有最大值 那麼枚舉的後續都是一樣的。ok上代碼
#include<iostream>
#include<cstdio>
#include<map>
#include<cstring>
#include<string>
#include<stack>
#include<queue>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
#define LL long long
LL dp[20][20][2050];
int dig[20];
LL dfs(int len,int pos,int sum,bool haveMax)
{
if(len<0)
return sum==0?1:0;
if(!haveMax && dp[len][pos][sum]!=-1)
return dp[len][pos][sum];
int maxNum = haveMax?dig[len]:9;
LL ans = 0;
for(int i = 0;i<=maxNum;i++)
{
ans+=dfs(len-1,pos,sum+(len-pos)*i,haveMax&&i==maxNum);
}
if(!haveMax)
{
dp[len][pos][sum] = ans;
}
return ans;
}
LL solve(LL n)
{
int len = 0;
while(n)
{
dig[len++] = n%10;
n/=10;
}
LL ans = 0;
for(int i = 0;i<len;i++)
{
ans+=dfs(len-1,i,0,true);
//printf("%lld\n",ans);
}
return ans-(len-1);
}
int main()
{
int t;
memset(dp,-1,sizeof(dp));
cin >> t;
while(t--)
{
LL l,r;
scanf("%lld%lld",&l,&r);
printf("%lld\n",solve(r)-solve(l-1));
}
return 0;
}