數位dp對於狀態描述與發現的一些感悟
今天刷的數位dp 第一題看了題解以後知道了數位dp的基本板子,寫數位dp的方式(運用記憶化遞歸的方法)已經基本固定。
那麽接下來的難點主要還是對於題目描述的問題,如何抽象成dp中的狀態。就今天刷的題來看,dp數組第一維一般為第i位數,這是數位dp的一般表示數的方式。而數組究竟還要加幾維就要看題目要求的東西。
如下題:
http://acm.hdu.edu.cn/showproblem.php?pid=3709
題意即求:
對於某個 number,你可以 fix a pivot 在某位,然後如果分成的左右兩部分的 sigma(d[i] * | i - fixloc |)相等,則它是 Balanced Number。
統計區間 [a,b] 中Balance Number 的個數。(引用https://blog.csdn.net/dgq8211/article/details/9302069)
這個題目問的是滿足條件的最大總數。故dp數組(不管幾維)=當前狀態的最大總數。
那麽問題用數位dp 關註數字本身屬性的思想,可以抽象出一個狀態即支點的位置[x]。
//除此,與問題結果直接相關的即支點兩端的力矩差[sum]。
因為數位dp是(貌似dp都是)枚舉所有情況再找到最優解的,所以一定會有一個狀態為問題所要求滿足的東西。以該題看,條件要求滿足的就是支點兩邊的力矩差=0,那麽力矩差就一定是dp數組裏的一維了。
綜上兩個狀態發現最大總數就是各個點為支點所有滿足力矩差為0的情況的總和sum。
可以將所有的情況列出。此時思考dp數組結束,即dp[pos][x][sum]。
找到正確的dp數組後就可以開心的套板子再調整細節等AC了~~~
(這題AC代碼可以從上面的鏈接看,我偷懶不發了)
其實如果能將問題抽象為數學函數那麽能更加直觀的發現狀態,如題目要求找到(0,n)上所有能被13整除的數的總數那滿足要求的x=cigma(n%13==0) dp數組即cigma(),狀態就是數n和n%13,(也就是自變量和與自變量相關的運算)。
可得dp[pos][13]。
#include<cstdio> #include<cstring> int dp[10][15];int digit[10]; int n; int dfs(int pos,int mod ,int limit) { if(pos==-1)return mod==0; if(!limit&&dp[pos][mod]!=-1)return dp[pos][mod]; int tmp=0,mm,; int up=limit?digit[pos]:9; for(int i=0;i<=up;i++) { mm=(mod*10+i)%13; tmp+=dfs(pos-1,mm,limit&&i==digit[pos]); } if(!limit)dp[pos][mod]=tmp; return tmp; } int solve(int x) { int k = x; int pos = 0; while(k) { digit[pos++]=k%10; k/=10; } return dfs(pos-1,0,1); } int main() { memset(dp,-1,sizeof(dp)); while(~scanf("%d",&n)) { printf("%d\n",solve(n)); } return 0; }
數位dp對於狀態描述與發現的一些感悟