数位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;
}

 

posted @ 2018-07-21 00:11  llllrj  阅读(476)  评论(0编辑  收藏  举报